From bb58f9a943cef24178e719354a701fa7f5faf524 Mon Sep 17 00:00:00 2001
From: Stefan Bodewig
Date: Mon, 23 Apr 2001 16:12:25 +0000
Subject: [PATCH] Reimplementation of ZipOutputStream along with a subclass of
ZipEntry to allow Ant generated zip/jar/war/ear files to be extracted on Unix
boxes without permission problems for directories.
This one uses external file attributes like InfoZip's zip does, this
is a rough cut - the infrastructure to set arbitrary permissions and
add user/group IDs via Zip's external fields is there, but not used
ATM. Directories will always get 755 permissions right now.
The testcases work on my box, and I can extract Ant created archives
without any trouble - can't await tomorrows Gump run.
package documentation for org.apache.tools.zip will follow.
git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@268959 13f79535-47bb-0310-9956-ffa450edef68
---
WHATSNEW | 3 +
.../org/apache/tools/ant/taskdefs/Ear.java | 2 +-
.../org/apache/tools/ant/taskdefs/Jar.java | 2 +-
.../org/apache/tools/ant/taskdefs/War.java | 2 +-
.../org/apache/tools/ant/taskdefs/Zip.java | 10 +-
.../org/apache/tools/zip/AsiExtraField.java | 366 +++++++++++
.../org/apache/tools/zip/ExtraFieldUtils.java | 203 ++++++
src/main/org/apache/tools/zip/UnixStat.java | 113 ++++
.../tools/zip/UnrecognizedExtraField.java | 131 ++++
src/main/org/apache/tools/zip/ZipEntry.java | 272 ++++++++
.../org/apache/tools/zip/ZipExtraField.java | 119 ++++
src/main/org/apache/tools/zip/ZipLong.java | 142 ++++
.../org/apache/tools/zip/ZipOutputStream.java | 619 ++++++++++++++++++
src/main/org/apache/tools/zip/ZipShort.java | 138 ++++
.../apache/tools/zip/AsiExtraFieldTest.java | 178 +++++
.../apache/tools/zip/ExtraFieldUtilsTest.java | 151 +++++
.../org/apache/tools/zip/ZipEntryTest.java | 124 ++++
.../org/apache/tools/zip/ZipLongTest.java | 119 ++++
.../org/apache/tools/zip/ZipShortTest.java | 117 ++++
19 files changed, 2806 insertions(+), 5 deletions(-)
create mode 100644 src/main/org/apache/tools/zip/AsiExtraField.java
create mode 100644 src/main/org/apache/tools/zip/ExtraFieldUtils.java
create mode 100644 src/main/org/apache/tools/zip/UnixStat.java
create mode 100644 src/main/org/apache/tools/zip/UnrecognizedExtraField.java
create mode 100644 src/main/org/apache/tools/zip/ZipEntry.java
create mode 100644 src/main/org/apache/tools/zip/ZipExtraField.java
create mode 100644 src/main/org/apache/tools/zip/ZipLong.java
create mode 100644 src/main/org/apache/tools/zip/ZipOutputStream.java
create mode 100644 src/main/org/apache/tools/zip/ZipShort.java
create mode 100644 src/testcases/org/apache/tools/zip/AsiExtraFieldTest.java
create mode 100644 src/testcases/org/apache/tools/zip/ExtraFieldUtilsTest.java
create mode 100644 src/testcases/org/apache/tools/zip/ZipEntryTest.java
create mode 100644 src/testcases/org/apache/tools/zip/ZipLongTest.java
create mode 100644 src/testcases/org/apache/tools/zip/ZipShortTest.java
diff --git a/WHATSNEW b/WHATSNEW
index 1771d11a5..f6fe64fa0 100644
--- a/WHATSNEW
+++ b/WHATSNEW
@@ -10,6 +10,9 @@ Changes that could break older environments:
rmic to use a new compiler a lot easier but may break custom
versions of this task that rely on the old implementation.
+* several Zip methods have changed their signature as we now use a Zip
+ package of our own that handles Unix permissions for directories.
+
Other changes:
--------------
diff --git a/src/main/org/apache/tools/ant/taskdefs/Ear.java b/src/main/org/apache/tools/ant/taskdefs/Ear.java
index a52e6b676..cac185cab 100644
--- a/src/main/org/apache/tools/ant/taskdefs/Ear.java
+++ b/src/main/org/apache/tools/ant/taskdefs/Ear.java
@@ -55,10 +55,10 @@ package org.apache.tools.ant.taskdefs;
import org.apache.tools.ant.*;
import org.apache.tools.ant.types.ZipFileSet;
+import org.apache.tools.zip.*;
import java.io.*;
import java.util.Vector;
-import java.util.zip.*;
/**
* Creates a EAR archive. Based on WAR task
diff --git a/src/main/org/apache/tools/ant/taskdefs/Jar.java b/src/main/org/apache/tools/ant/taskdefs/Jar.java
index d69dd4289..8134e6661 100644
--- a/src/main/org/apache/tools/ant/taskdefs/Jar.java
+++ b/src/main/org/apache/tools/ant/taskdefs/Jar.java
@@ -56,9 +56,9 @@ package org.apache.tools.ant.taskdefs;
import org.apache.tools.ant.*;
import org.apache.tools.ant.types.ZipFileSet;
+import org.apache.tools.zip.*;
import java.io.*;
-import java.util.zip.*;
/**
* Creates a JAR archive.
diff --git a/src/main/org/apache/tools/ant/taskdefs/War.java b/src/main/org/apache/tools/ant/taskdefs/War.java
index 98f23bcde..7e12d545e 100644
--- a/src/main/org/apache/tools/ant/taskdefs/War.java
+++ b/src/main/org/apache/tools/ant/taskdefs/War.java
@@ -56,10 +56,10 @@ package org.apache.tools.ant.taskdefs;
import org.apache.tools.ant.*;
import org.apache.tools.ant.types.ZipFileSet;
+import org.apache.tools.zip.*;
import java.io.*;
import java.util.Vector;
-import java.util.zip.*;
/**
* Creates a WAR archive.
diff --git a/src/main/org/apache/tools/ant/taskdefs/Zip.java b/src/main/org/apache/tools/ant/taskdefs/Zip.java
index 2acbf56d6..4536406e4 100644
--- a/src/main/org/apache/tools/ant/taskdefs/Zip.java
+++ b/src/main/org/apache/tools/ant/taskdefs/Zip.java
@@ -59,10 +59,12 @@ import java.util.Hashtable;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.Vector;
-import java.util.zip.*;
+import java.util.zip.CRC32;
+import java.util.zip.ZipInputStream;
import org.apache.tools.ant.*;
import org.apache.tools.ant.types.*;
import org.apache.tools.ant.util.*;
+import org.apache.tools.zip.*;
/**
* Create a ZIP archive.
@@ -280,7 +282,7 @@ public class Zip extends MatchingTask {
try {
in = new ZipInputStream(new FileInputStream(zipSrc));
- while ((entry = in.getNextEntry()) != null) {
+ while ((entry = new ZipEntry(in.getNextEntry())) != null) {
String vPath = entry.getName();
if (zipScanner.match(vPath)) {
addParentDirs(null, vPath, zOut, prefix);
@@ -414,6 +416,10 @@ public class Zip extends MatchingTask {
ze.setMethod (ZipEntry.STORED);
// This is faintly ridiculous:
ze.setCrc (emptyCrc);
+
+ // this is 040775 | MS-DOS directory flag in reverse byte order
+ ze.setExternalAttributes(0x41FD0010L);
+
zOut.putNextEntry (ze);
}
diff --git a/src/main/org/apache/tools/zip/AsiExtraField.java b/src/main/org/apache/tools/zip/AsiExtraField.java
new file mode 100644
index 000000000..111c6d905
--- /dev/null
+++ b/src/main/org/apache/tools/zip/AsiExtraField.java
@@ -0,0 +1,366 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2001 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowlegement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Ant", and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ */
+
+package org.apache.tools.zip;
+
+import java.util.zip.CRC32;
+import java.util.zip.ZipException;
+
+/**
+ * Adds Unix file permission and UID/GID fields as well as symbolic
+ * link handling.
+ *
+ * This class uses the ASi extra field in the format:
+ *
+ * Value Size Description
+ * ----- ---- -----------
+ * (Unix3) 0x756e Short tag for this extra block type
+ * TSize Short total data size for this block
+ * CRC Long CRC-32 of the remaining data
+ * Mode Short file permissions
+ * SizDev Long symlink'd size OR major/minor dev num
+ * UID Short user ID
+ * GID Short group ID
+ * (var.) variable symbolic link filename
+ *
+ * taken from appnote.iz (Info-ZIP note, 981119) found at ftp://ftp.uu.net/pub/archiving/zip/doc/
+
+ *
+ * Short is two bytes and Long is four bytes in big endian byte and
+ * word order, device numbers are currently not supported.
+ *
+ * @author Stefan Bodewig
+ * @version $Revision$
+ */
+public class AsiExtraField implements ZipExtraField, UnixStat, Cloneable {
+
+ private final static ZipShort HEADER_ID = new ZipShort(0x756E);
+
+ /**
+ * Standard Unix stat(2) file mode.
+ *
+ * @since 1.1
+ */
+ private int mode = 0;
+ /**
+ * User ID.
+ *
+ * @since 1.1
+ */
+ private int uid = 0;
+ /**
+ * Group ID.
+ *
+ * @since 1.1
+ */
+ private int gid = 0;
+ /**
+ * File this entry points to, if it is a symbolic link.
+ *
+ * empty string - if entry is not a symbolic link.
+ *
+ * @since 1.1
+ */
+ private String link = "";
+ /**
+ * Is this an entry for a directory?
+ *
+ * @since 1.1
+ */
+ private boolean dirFlag = false;
+
+ /**
+ * Instance used to calculate checksums.
+ *
+ * @since 1.1
+ */
+ private CRC32 crc = new CRC32();
+
+ public AsiExtraField() {
+ }
+
+ /**
+ * The Header-ID.
+ *
+ * @since 1.1
+ */
+ public ZipShort getHeaderId() {
+ return HEADER_ID;
+ }
+
+ /**
+ * Length of the extra field in the local file data - without
+ * Header-ID or length specifier.
+ *
+ * @since 1.1
+ */
+ public ZipShort getLocalFileDataLength() {
+ return new ZipShort( 4 // CRC
+ + 2 // Mode
+ + 4 // SizDev
+ + 2 // UID
+ + 2 // GID
+ + getLinkedFile().getBytes().length);
+ }
+
+ /**
+ * Delegate to local file data.
+ *
+ * @since 1.1
+ */
+ public ZipShort getCentralDirectoryLength() {
+ return getLocalFileDataLength();
+ }
+
+ /**
+ * The actual data to put into local file data - without Header-ID
+ * or length specifier.
+ *
+ * @since 1.1
+ */
+ public byte[] getLocalFileDataData() {
+ // CRC will be added later
+ byte[] data = new byte[getLocalFileDataLength().getValue() - 4];
+ System.arraycopy((new ZipShort(getMode())).getBytes(), 0, data, 0, 2);
+
+ byte[] linkArray = getLinkedFile().getBytes();
+ System.arraycopy((new ZipLong(linkArray.length)).getBytes(),
+ 0, data, 2, 4);
+
+ System.arraycopy((new ZipShort(getUserId())).getBytes(),
+ 0, data, 6, 2);
+ System.arraycopy((new ZipShort(getGroupId())).getBytes(),
+ 0, data, 8, 2);
+
+ System.arraycopy(linkArray, 0, data, 10, linkArray.length);
+
+ crc.reset();
+ crc.update(data);
+ long checksum = crc.getValue();
+
+ byte[] result = new byte[data.length + 4];
+ System.arraycopy((new ZipLong(checksum)).getBytes(), 0, result, 0, 4);
+ System.arraycopy(data, 0, result, 4, data.length);
+ return result;
+ }
+
+ /**
+ * Delegate to local file data.
+ *
+ * @since 1.1
+ */
+ public byte[] getCentralDirectoryData() {
+ return getLocalFileDataData();
+ }
+
+ /**
+ * Set the user id.
+ *
+ * @since 1.1
+ */
+ public void setUserId(int uid) {
+ this.uid = uid;
+ }
+
+ /**
+ * Get the user id.
+ *
+ * @since 1.1
+ */
+ public int getUserId() {
+ return uid;
+ }
+
+ /**
+ * Set the group id.
+ *
+ * @since 1.1
+ */
+ public void setGroupId(int gid) {
+ this.gid = gid;
+ }
+
+ /**
+ * Get the group id.
+ *
+ * @since 1.1
+ */
+ public int getGroupId() {
+ return gid;
+ }
+
+ /**
+ * Indicate that this entry is a symbolic link to the given filename.
+ *
+ * @param name Name of the file this entry links to, empty String
+ * if it is not a symbolic link.
+ *
+ * @since 1.1
+ */
+ public void setLinkedFile(String name) {
+ link = name;
+ mode = getMode(mode);
+ }
+
+ /**
+ * Name of linked file
+ *
+ * @return name of the file this entry links to if it is a
+ * symbolic link, the empty string otherwise.
+ *
+ * @since 1.1
+ */
+ public String getLinkedFile() {
+ return link;
+ }
+
+ /**
+ * Is this entry a symbolic link?
+ *
+ * @since 1.1
+ */
+ public boolean isLink() {
+ return getLinkedFile().length() != 0;
+ }
+
+ /**
+ * File mode of this file.
+ *
+ * @since 1.1
+ */
+ public void setMode(int mode) {
+ this.mode = getMode(mode);
+ }
+
+ /**
+ * File mode of this file.
+ *
+ * @since 1.1
+ */
+ public int getMode() {
+ return mode;
+ }
+
+ /**
+ * Indicate whether this entry is a directory.
+ *
+ * @since 1.1
+ */
+ public void setDirectory(boolean dirFlag) {
+ this.dirFlag = dirFlag;
+ mode = getMode(mode);
+ }
+
+ /**
+ * Is this entry a directory?
+ *
+ * @since 1.1
+ */
+ public boolean isDirectory() {
+ return dirFlag && !isLink();
+ }
+
+ /**
+ * Populate data from this array as if it was in local file data.
+ *
+ * @since 1.1
+ */
+ public void parseFromLocalFileData(byte[] data, int offset, int length)
+ throws ZipException {
+
+ long givenChecksum = (new ZipLong(data, offset)).getValue();
+ byte[] tmp = new byte[length-4];
+ System.arraycopy(data, offset+4, tmp, 0, length-4);
+ crc.reset();
+ crc.update(tmp);
+ long realChecksum = crc.getValue();
+ if (givenChecksum != realChecksum) {
+ throw new ZipException("bad CRC checksum "
+ + Long.toHexString(givenChecksum)
+ + " instead of "
+ + Long.toHexString(realChecksum));
+ }
+
+ int newMode = (new ZipShort(tmp, 0)).getValue();
+ byte[] linkArray = new byte[(int) (new ZipLong(tmp, 2)).getValue()];
+ uid = (new ZipShort(tmp, 6)).getValue();
+ gid = (new ZipShort(tmp, 8)).getValue();
+
+ if (linkArray.length == 0) {
+ link = "";
+ } else {
+ System.arraycopy(tmp, 10, linkArray, 0, linkArray.length);
+ link = new String(linkArray);
+ }
+ setDirectory((newMode & DIR_FLAG) != 0);
+ setMode(newMode);
+ }
+
+ /**
+ * Get the file mode for given permissions with the correct file type.
+ *
+ * @since 1.1
+ */
+ protected int getMode(int mode) {
+ int type = FILE_FLAG;
+ if (isLink()) {
+ type = LINK_FLAG;
+ } else if (isDirectory()) {
+ type = DIR_FLAG;
+ }
+ return type | (mode & PERM_MASK);
+ }
+
+}
diff --git a/src/main/org/apache/tools/zip/ExtraFieldUtils.java b/src/main/org/apache/tools/zip/ExtraFieldUtils.java
new file mode 100644
index 000000000..005e98809
--- /dev/null
+++ b/src/main/org/apache/tools/zip/ExtraFieldUtils.java
@@ -0,0 +1,203 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2001 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowlegement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Ant", and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ */
+
+package org.apache.tools.zip;
+
+import java.util.Hashtable;
+import java.util.Vector;
+import java.util.zip.ZipException;
+
+/**
+ * ZipExtraField related methods
+ *
+ * @author Stefan Bodewig
+ * @version $Revision$
+ */
+public class ExtraFieldUtils {
+
+ /**
+ * Static registry of known extra fields.
+ *
+ * @since 1.1
+ */
+ private static Hashtable implementations;
+
+ static {
+ implementations = new Hashtable();
+ register(AsiExtraField.class);
+ }
+
+ /**
+ * Register a ZipExtraField implementation.
+ *
+ * The given class must have a no-arg constructor and implement
+ * the {@link ZipExtraField ZipExtraField interface}.
+ *
+ * @since 1.1
+ */
+ public static void register(Class c) {
+ try {
+ ZipExtraField ze = (ZipExtraField) c.newInstance();
+ implementations.put(ze.getHeaderId(), c);
+ } catch (ClassCastException cc) {
+ throw new RuntimeException(c +
+ " doesn\'t implement ZipExtraField");
+ } catch (InstantiationException ie) {
+ throw new RuntimeException(c + " is not a concrete class");
+ } catch (IllegalAccessException ie) {
+ throw new RuntimeException(c +
+ "\'s no-arg constructor is not public");
+ }
+ }
+
+ /**
+ * Create an instance of the approriate ExtraField, falls back to
+ * {@link UnrecognizedExtraField UnrecognizedExtraField}.
+ *
+ * @since 1.1
+ */
+ public static ZipExtraField createExtraField(ZipShort headerId)
+ throws InstantiationException, IllegalAccessException {
+ Class c = (Class) implementations.get(headerId);
+ if (c != null) {
+ return (ZipExtraField) c.newInstance();
+ }
+ UnrecognizedExtraField u = new UnrecognizedExtraField();
+ u.setHeaderId(headerId);
+ return u;
+ }
+
+ /**
+ * Split the array into ExtraFields and populate them with the
+ * give data.
+ *
+ * @since 1.1
+ */
+ public static ZipExtraField[] parse(byte[] data) throws ZipException {
+ Vector v = new Vector();
+ int start = 0;
+ while (start <= data.length-4) {
+ ZipShort headerId = new ZipShort(data, start);
+ int length = (new ZipShort(data, start+2)).getValue();
+ if (start+4+length > data.length) {
+ throw new ZipException("data starting at "+start+" is in unknown format");
+ }
+ try {
+ ZipExtraField ze = createExtraField(headerId);
+ ze.parseFromLocalFileData(data, start+4, length);
+ v.addElement(ze);
+ } catch (InstantiationException ie) {
+ throw new ZipException(ie.getMessage());
+ } catch (IllegalAccessException iae) {
+ throw new ZipException(iae.getMessage());
+ }
+ start += (length+4);
+ }
+ if (start != data.length) { // array not exhausted
+ throw new ZipException("data starting at "+start+" is in unknown format");
+ }
+
+ ZipExtraField[] result = new ZipExtraField[v.size()];
+ v.copyInto(result);
+ return result;
+ }
+
+ /**
+ * Merges the local file data fields of the given ZipExtraFields.
+ *
+ * @since 1.1
+ */
+ public static byte[] mergeLocalFileDataData(ZipExtraField[] data) {
+ int sum = 4*data.length;
+ for (int i=0; i.
+ */
+
+package org.apache.tools.zip;
+
+/**
+ * Constants from stat.h on Unix systems.
+ *
+ * @author Stefan Bodewig
+ * @version $Revision$
+ */
+public interface UnixStat {
+
+ /**
+ * Bits used for permissions (and sticky bit)
+ *
+ * @since 1.1
+ */
+ public static final int PERM_MASK = 07777;
+ /**
+ * Indicates symbolic links.
+ *
+ * @since 1.1
+ */
+ public static final int LINK_FLAG = 0120000;
+ /**
+ * Indicates plain files.
+ *
+ * @since 1.1
+ */
+ public static final int FILE_FLAG = 0100000;
+ /**
+ * Indicates directories.
+ *
+ * @since 1.1
+ */
+ public static final int DIR_FLAG = 040000;
+
+ // ----------------------------------------------------------
+ // somewhat arbitrary choices that are quite common for shared
+ // installations
+ // -----------------------------------------------------------
+
+ /**
+ * Default permissions for symbolic links.
+ *
+ * @since 1.1
+ */
+ public static final int DEFAULT_LINK_PERM = 0777;
+ /**
+ * Default permissions for directories.
+ *
+ * @since 1.1
+ */
+ public static final int DEFAULT_DIR_PERM = 0755;
+ /**
+ * Default permissions for plain files.
+ *
+ * @since 1.1
+ */
+ public static final int DEFAULT_FILE_PERM = 0644;
+}
diff --git a/src/main/org/apache/tools/zip/UnrecognizedExtraField.java b/src/main/org/apache/tools/zip/UnrecognizedExtraField.java
new file mode 100644
index 000000000..d15c37106
--- /dev/null
+++ b/src/main/org/apache/tools/zip/UnrecognizedExtraField.java
@@ -0,0 +1,131 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2001 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowlegement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Ant", and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ */
+
+package org.apache.tools.zip;
+
+/**
+ * Simple placeholder for all those extra fields we don't want to deal
+ * with.
+ *
+ * Assumes local file data and central directory entries are
+ * identical - unless told the opposite.
+ *
+ * @author Stefan Bodewig
+ * @version $Revision$
+ */
+public class UnrecognizedExtraField implements ZipExtraField {
+
+ /**
+ * The Header-ID.
+ *
+ * @since 1.1
+ */
+ private ZipShort headerId;
+
+ public void setHeaderId(ZipShort headerId) {
+ this.headerId = headerId;
+ }
+
+ public ZipShort getHeaderId() {return headerId;}
+
+ /**
+ * Extra field data in local file data - without
+ * Header-ID or length specifier.
+ *
+ * @since 1.1
+ */
+ private byte[] localData;
+
+ public void setLocalFileDataData(byte[] data) {
+ localData = data;
+ }
+
+ public ZipShort getLocalFileDataLength() {
+ return new ZipShort(localData.length);
+ }
+
+ public byte[] getLocalFileDataData() {return localData;}
+
+ /**
+ * Extra field data in central directory - without
+ * Header-ID or length specifier.
+ *
+ * @since 1.1
+ */
+ private byte[] centralData;
+
+ public void setCentralDirectoryData(byte[] data) {
+ centralData = data;
+ }
+
+ public ZipShort getCentralDirectoryLength() {
+ if (centralData != null) {
+ return new ZipShort(centralData.length);
+ }
+ return getLocalFileDataLength();
+ }
+
+ public byte[] getCentralDirectoryData() {
+ if (centralData != null) {
+ return centralData;
+ }
+ return getLocalFileDataData();
+ }
+
+ public void parseFromLocalFileData(byte[] data, int offset, int length) {
+ byte[] tmp = new byte[length];
+ System.arraycopy(data, offset, tmp, 0, length);
+ setLocalFileDataData(tmp);
+ }
+}
diff --git a/src/main/org/apache/tools/zip/ZipEntry.java b/src/main/org/apache/tools/zip/ZipEntry.java
new file mode 100644
index 000000000..46bae0be3
--- /dev/null
+++ b/src/main/org/apache/tools/zip/ZipEntry.java
@@ -0,0 +1,272 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2001 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowlegement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Ant", and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ */
+
+package org.apache.tools.zip;
+
+import java.util.Vector;
+import java.util.zip.ZipException;
+
+/**
+ * Extension that adds better handling of extra fields and provides
+ * access to the internal and external file attributes.
+ *
+ * @author Stefan Bodewig
+ * @version $Revision$
+ */
+public class ZipEntry extends java.util.zip.ZipEntry {
+
+ private int internalAttributes = 0;
+ private long externalAttributes = 0;
+ private Vector extraFields = new Vector();
+
+ /**
+ * Creates a new zip entry with the specified name.
+ *
+ * @since 1.1
+ */
+ public ZipEntry(String name) {
+ super(name);
+ }
+
+ /**
+ * Creates a new zip entry with fields taken from the specified zip entry.
+ *
+ * @since 1.1
+ */
+ public ZipEntry(java.util.zip.ZipEntry entry) throws ZipException {
+ super(entry);
+ byte[] extra = entry.getExtra();
+ if (extra != null) {
+ setExtraFields(ExtraFieldUtils.parse(extra));
+ } else {
+ // initializes extra data to an empty byte array
+ setExtra();
+ }
+ }
+
+ /**
+ * Creates a new zip entry with fields taken from the specified zip entry.
+ *
+ * @since 1.1
+ */
+ public ZipEntry(ZipEntry entry) {
+ super(entry);
+ setInternalAttributes(entry.getInternalAttributes());
+ setExternalAttributes(entry.getExternalAttributes());
+ setExtraFields(entry.getExtraFields());
+ }
+
+ /**
+ * Overwrite clone
+ *
+ * @since 1.1
+ */
+ public Object clone() {
+ ZipEntry e = null;
+ try {
+ e = new ZipEntry((java.util.zip.ZipEntry) super.clone());
+ } catch (Exception ex) {
+ // impossible as extra data is in correct format
+ ex.printStackTrace();
+ }
+ e.setInternalAttributes(getInternalAttributes());
+ e.setExternalAttributes(getExternalAttributes());
+ e.setExtraFields(getExtraFields());
+ return e;
+ }
+
+ /**
+ * Retrieves the internal file attributes.
+ *
+ * @since 1.1
+ */
+ public int getInternalAttributes() {
+ return internalAttributes;
+ }
+
+ /**
+ * Sets the internal file attributes.
+ *
+ * @since 1.1
+ */
+ public void setInternalAttributes(int value) {
+ internalAttributes = value;
+ }
+
+ /**
+ * Retrieves the external file attributes.
+ *
+ * @since 1.1
+ */
+ public long getExternalAttributes() {
+ return externalAttributes;
+ }
+
+ /**
+ * Sets the external file attributes.
+ *
+ * @since 1.1
+ */
+ public void setExternalAttributes(long value) {
+ externalAttributes = value;
+ }
+
+ /**
+ * Replaces all currently attached extra fields with the new array.
+ *
+ * @since 1.1
+ */
+ public void setExtraFields(ZipExtraField[] fields) {
+ extraFields.removeAllElements();
+ for (int i=0; i.
+ */
+
+package org.apache.tools.zip;
+
+import java.util.zip.ZipException;
+
+/**
+ * General format of extra field data.
+ *
+ * Extra fields usually apper twice per file, once in the local
+ * file data and once in the central directory. Usually they are the
+ * same, but they don't have to be. {@link
+ * java.util.zip.ZipOutputStream java.util.zip.ZipOutputStream} will
+ * only use write the local file data at both places.
+ *
+ * @author Stefan Bodewig
+ * @version $Revision$
+ */
+public interface ZipExtraField {
+
+ /**
+ * The Header-ID.
+ *
+ * @since 1.1
+ */
+ public ZipShort getHeaderId();
+
+ /**
+ * Length of the extra field in the local file data - without
+ * Header-ID or length specifier.
+ *
+ * @since 1.1
+ */
+ public ZipShort getLocalFileDataLength();
+
+ /**
+ * Length of the extra field in the central directory - without
+ * Header-ID or length specifier.
+ *
+ * @since 1.1
+ */
+ public ZipShort getCentralDirectoryLength();
+
+ /**
+ * The actual data to put into local file data - without Header-ID
+ * or length specifier.
+ *
+ * @since 1.1
+ */
+ public byte[] getLocalFileDataData();
+
+ /**
+ * The actual data to put central directory - without Header-ID or
+ * length specifier.
+ *
+ * @since 1.1
+ */
+ public byte[] getCentralDirectoryData();
+
+ /**
+ * Populate data from this array as if it was in local file data.
+ *
+ * @since 1.1
+ */
+ public void parseFromLocalFileData(byte[] data, int offset, int length)
+ throws ZipException;
+}
diff --git a/src/main/org/apache/tools/zip/ZipLong.java b/src/main/org/apache/tools/zip/ZipLong.java
new file mode 100644
index 000000000..565bf03d7
--- /dev/null
+++ b/src/main/org/apache/tools/zip/ZipLong.java
@@ -0,0 +1,142 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2001 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowlegement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Ant", and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ */
+
+package org.apache.tools.zip;
+
+/**
+ * Utility class that represents a four byte integer with conversion
+ * rules for the big endian byte order of ZIP files.
+ *
+ * @author Stefan Bodewig
+ * @version $Revision$
+ */
+public class ZipLong implements Cloneable {
+
+ private long value;
+
+ /**
+ * Create instance from a number.
+ *
+ * @since 1.1
+ */
+ public ZipLong(long value) {
+ this.value = value;
+ }
+
+ /**
+ * Create instance from bytes.
+ *
+ * @since 1.1
+ */
+ public ZipLong (byte[] bytes) {
+ this(bytes, 0);
+ }
+
+ /**
+ * Create instance from the four bytes starting at offset.
+ *
+ * @since 1.1
+ */
+ public ZipLong (byte[] bytes, int offset) {
+ value = (bytes[offset+3] << 24) & 0xFF000000l;
+ value += (bytes[offset+2] << 16) & 0xFF0000;
+ value += (bytes[offset+1] << 8) & 0xFF00;
+ value += (bytes[offset] & 0xFF);
+ }
+
+ /**
+ * Get value as two bytes in big endian byte order.
+ *
+ * @since 1.1
+ */
+ public byte[] getBytes() {
+ byte[] result = new byte[4];
+ result[0] = (byte) ((value & 0xFF));
+ result[1] = (byte) ((value & 0xFF00) >> 8);
+ result[2] = (byte) ((value & 0xFF0000) >> 16);
+ result[3] = (byte) ((value & 0xFF000000l) >> 24);
+ return result;
+ }
+
+ /**
+ * Get value as Java int.
+ *
+ * @since 1.1
+ */
+ public long getValue() {
+ return value;
+ }
+
+ /**
+ * Override to make two instances with same value equal.
+ *
+ * @since 1.1
+ */
+ public boolean equals(Object o) {
+ if (o == null || !(o instanceof ZipLong)) {
+ return false;
+ }
+ return value == ((ZipLong) o).getValue();
+ }
+
+ /**
+ * Override to make two instances with same value equal.
+ *
+ * @since 1.1
+ */
+ public int hashCode() {
+ return (int) value;
+ }
+
+}// ZipLong
diff --git a/src/main/org/apache/tools/zip/ZipOutputStream.java b/src/main/org/apache/tools/zip/ZipOutputStream.java
new file mode 100644
index 000000000..e7a5e689b
--- /dev/null
+++ b/src/main/org/apache/tools/zip/ZipOutputStream.java
@@ -0,0 +1,619 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2001 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowlegement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Ant", and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ */
+
+package org.apache.tools.zip;
+
+import java.io.*;
+import java.util.Date;
+import java.util.Hashtable;
+import java.util.Vector;
+import java.util.zip.CRC32;
+import java.util.zip.Deflater;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.ZipException;
+
+/**
+ * Reimplementation of {@link java.util.zip.ZipOutputStream
+ * java.util.zip.ZipOutputStream} that does handle the extended
+ * functionality of this package, especially internal/external file
+ * attributes and extra fields with different layouts for local file
+ * data and central directory entries.
+ *
+ * This implementation will use a Data Descriptor to store size and
+ * CRC information for DEFLATED entries, this means, you don't need to
+ * calculate them yourself. Unfortunately this is not possible for
+ * the STORED method, here setting the CRC and uncompressed size
+ * information is required before {@link #putNextEntry putNextEntry}
+ * will be called.
+ *
+ * @author Stefan Bodewig
+ * @version $Revision$
+ */
+public class ZipOutputStream extends DeflaterOutputStream {
+
+ /**
+ * Current entry.
+ *
+ * @since 1.1
+ */
+ private ZipEntry entry;
+
+ /**
+ * The file comment.
+ *
+ * @since 1.1
+ */
+ private String comment = "";
+
+ /**
+ * Compression level for next entry.
+ *
+ * @since 1.1
+ */
+ private int level = Deflater.DEFAULT_COMPRESSION;
+
+ /**
+ * Default compression method for next entry.
+ *
+ * @since 1.1
+ */
+ private int method = DEFLATED;
+
+ /**
+ * List of ZipEntries written so far.
+ *
+ * @since 1.1
+ */
+ private Vector entries = new Vector();
+
+ /**
+ * CRC instance to avoid parsing DEFLATED data twice.
+ *
+ * @since 1.1
+ */
+ private CRC32 crc = new CRC32();
+
+ /**
+ * Count the bytes written to out.
+ *
+ * @since 1.1
+ */
+ private long written = 0;
+
+ /**
+ * Data for current entry started here.
+ *
+ * @since 1.1
+ */
+ private long dataStart = 0;
+
+ /**
+ * Start of central directory.
+ *
+ * @since 1.1
+ */
+ private ZipLong cdOffset = new ZipLong(0);
+
+ /**
+ * Length of central directory.
+ *
+ * @since 1.1
+ */
+ private ZipLong cdLength = new ZipLong(0);
+
+ /**
+ * Helper, a 0 as ZipShort.
+ *
+ * @since 1.1
+ */
+ private static final byte[] ZERO = {0, 0};
+
+ /**
+ * Helper, a 0 as ZipLong.
+ *
+ * @since 1.1
+ */
+ private static final byte[] LZERO = {0, 0, 0, 0};
+
+ /**
+ * Holds the offsets of the LFH starts for each entry
+ *
+ * @since 1.1
+ */
+ private Hashtable offsets = new Hashtable();
+
+ /**
+ * Compression method for deflated entries.
+ *
+ * @since 1.1
+ */
+ public static final int DEFLATED = ZipEntry.DEFLATED;
+
+ /**
+ * Compression method for deflated entries.
+ *
+ * @since 1.1
+ */
+ public static final int STORED = ZipEntry.STORED;
+
+ /**
+ * Creates a new ZIP OutputStream filtering the underlying stream.
+ *
+ * @since 1.1
+ */
+ public ZipOutputStream(OutputStream out) {
+ super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true));
+ }
+
+ /*
+ * Found out by experiment, that DeflaterOutputStream.close()
+ * will call finish() - so we don't need to override close
+ * ourselves.
+ */
+
+ /**
+ * Finishs writing the contents and closes this as well as the
+ * underlying stream.
+ *
+ * @since 1.1
+ */
+ public void finish() throws IOException {
+ closeEntry();
+ cdOffset = new ZipLong(written);
+ for (int i=0; iDefault is Deflater.DEFAULT_COMPRESSION.
+ *
+ * @since 1.1
+ */
+ public void setLevel(int level) {
+ this.level = level;
+ }
+
+ /**
+ * Sets the default compression method for subsequent entries.
+ *
+ * Default is DEFLATED.
+ *
+ * @since 1.1
+ */
+ public void setMethod(int method) {
+ this.method = method;
+ }
+
+ /**
+ * Writes bytes to ZIP entry.
+ *
+ * Override is necessary to support STORED entries, as well as
+ * calculationg CRC automatically for DEFLATED entries.
+ */
+ public void write(byte[] b, int offset, int length) throws IOException {
+ if (entry.getMethod() == DEFLATED) {
+ super.write(b, offset, length);
+ } else {
+ out.write(b, offset, length);
+ written += length;
+ }
+ crc.update(b, offset, length);
+ }
+
+ /*
+ * Various ZIP constants
+ */
+ /**
+ * local file header signature
+ *
+ * @since 1.1
+ */
+ protected static final ZipLong LFH_SIG = new ZipLong(0X04034B50L);
+ /**
+ * data descriptor signature
+ *
+ * @since 1.1
+ */
+ protected static final ZipLong DD_SIG = new ZipLong(0X08074B50L);
+ /**
+ * central file header signature
+ *
+ * @since 1.1
+ */
+ protected static final ZipLong CFH_SIG = new ZipLong(0X02014B50L);
+ /**
+ * end of central dir signature
+ *
+ * @since 1.1
+ */
+ protected static final ZipLong EOCD_SIG = new ZipLong(0X06054B50L);
+
+ /**
+ * Writes the local file header entry
+ *
+ * @since 1.1
+ */
+ protected void writeLocalFileHeader(ZipEntry ze) throws IOException {
+ offsets.put(ze, new ZipLong(written));
+
+ out.write(LFH_SIG.getBytes());
+ written += 4;
+
+ // version needed to extract
+ // general purpose bit flag
+ if (ze.getMethod() == DEFLATED) {
+ // requires version 2 as we are going to store length info
+ // in the data descriptor
+ out.write((new ZipShort(20)).getBytes());
+
+ // bit3 set to signal, we use a data descriptor
+ out.write((new ZipShort(8)).getBytes());
+ } else {
+ out.write((new ZipShort(10)).getBytes());
+ out.write(ZERO);
+ }
+ written += 4;
+
+ // compression method
+ out.write((new ZipShort(ze.getMethod())).getBytes());
+ written += 2;
+
+ // last mod. time and date
+ out.write(toDosTime(new Date(ze.getTime())).getBytes());
+ written += 4;
+
+ // CRC
+ // compressed length
+ // uncompressed length
+ if (ze.getMethod() == DEFLATED) {
+ out.write(LZERO);
+ out.write(LZERO);
+ out.write(LZERO);
+ } else {
+ out.write((new ZipLong(ze.getCrc())).getBytes());
+ out.write((new ZipLong(ze.getSize())).getBytes());
+ out.write((new ZipLong(ze.getSize())).getBytes());
+ }
+ written += 12;
+
+ // file name length
+ byte[] name = ze.getName().getBytes();
+ out.write((new ZipShort(name.length)).getBytes());
+ written += 2;
+
+ // extra field length
+ byte[] extra = ze.getLocalFileDataExtra();
+ out.write((new ZipShort(extra.length)).getBytes());
+ written += 2;
+
+ // file name
+ out.write(name);
+ written += name.length;
+
+ // extra field
+ out.write(extra);
+ written += extra.length;
+
+ dataStart = written;
+ }
+
+ /**
+ * Writes the data descriptor entry
+ *
+ * @since 1.1
+ */
+ protected void writeDataDescriptor(ZipEntry ze) throws IOException {
+ if (ze.getMethod() != DEFLATED) {
+ return;
+ }
+ out.write(DD_SIG.getBytes());
+ out.write((new ZipLong(entry.getCrc())).getBytes());
+ out.write((new ZipLong(entry.getCompressedSize())).getBytes());
+ out.write((new ZipLong(entry.getSize())).getBytes());
+ written += 16;
+ }
+
+ /**
+ * Writes the central file header entry
+ *
+ * @since 1.1
+ */
+ protected void writeCentralFileHeader(ZipEntry ze) throws IOException {
+ out.write(CFH_SIG.getBytes());
+ written += 4;
+
+ // version made by
+ out.write((new ZipShort(20)).getBytes());
+ written += 2;
+
+ // version needed to extract
+ // general purpose bit flag
+ if (ze.getMethod() == DEFLATED) {
+ // requires version 2 as we are going to store length info
+ // in the data descriptor
+ out.write((new ZipShort(20)).getBytes());
+
+ // bit3 set to signal, we use a data descriptor
+ out.write((new ZipShort(8)).getBytes());
+ } else {
+ out.write((new ZipShort(10)).getBytes());
+ out.write(ZERO);
+ }
+ written += 4;
+
+ // compression method
+ out.write((new ZipShort(ze.getMethod())).getBytes());
+ written += 2;
+
+ // last mod. time and date
+ out.write(toDosTime(new Date(ze.getTime())).getBytes());
+ written += 4;
+
+ // CRC
+ // compressed length
+ // uncompressed length
+ out.write((new ZipLong(ze.getCrc())).getBytes());
+ out.write((new ZipLong(ze.getCompressedSize())).getBytes());
+ out.write((new ZipLong(ze.getSize())).getBytes());
+ written += 12;
+
+ // file name length
+ byte[] name = ze.getName().getBytes();
+ out.write((new ZipShort(name.length)).getBytes());
+ written += 2;
+
+ // extra field length
+ byte[] extra = ze.getCentralDirectoryExtra();
+ out.write((new ZipShort(extra.length)).getBytes());
+ written += 2;
+
+ // file comment length
+ String comm = ze.getComment();
+ if (comm == null) {
+ comm = "";
+ }
+ byte[] comment = comm.getBytes();
+ out.write((new ZipShort(comment.length)).getBytes());
+ written += 2;
+
+ // disk number start
+ out.write(ZERO);
+ written += 2;
+
+ // internal file attributes
+ out.write((new ZipShort(ze.getInternalAttributes())).getBytes());
+ written += 2;
+
+ // external file attributes
+ out.write((new ZipLong(ze.getExternalAttributes())).getBytes());
+ written += 4;
+
+ // relative offset of LFH
+ out.write(((ZipLong) offsets.get(ze)).getBytes());
+ written += 4;
+
+ // file name
+ out.write(name);
+ written += name.length;
+
+ // extra field
+ out.write(extra);
+ written += extra.length;
+
+ // file comment
+ out.write(comment);
+ written += comment.length;
+ }
+
+ /**
+ * Writes the "End of central dir record"
+ *
+ * @since 1.1
+ */
+ protected void writeCentralDirectoryEnd() throws IOException {
+ out.write(EOCD_SIG.getBytes());
+
+ // disk numbers
+ out.write(ZERO);
+ out.write(ZERO);
+
+ // number of entries
+ byte[] num = (new ZipShort(entries.size())).getBytes();
+ out.write(num);
+ out.write(num);
+
+ // length and location of CD
+ out.write(cdLength.getBytes());
+ out.write(cdOffset.getBytes());
+
+ // ZIP file comment
+ byte[] data = comment.getBytes();
+ out.write((new ZipShort(data.length)).getBytes());
+ out.write(data);
+ }
+
+ /**
+ * Smallest date/time ZIP can handle.
+ *
+ * @since 1.1
+ */
+ private static final ZipLong DOS_TIME_MIN = new ZipLong(0x00002100L);
+
+ /**
+ * Convert a Date object to a DOS date/time field.
+ *
+ * Stolen from InfoZip's fileio.c
+ *
+ * @since 1.1
+ */
+ protected static ZipLong toDosTime(Date time) {
+ int year = time.getYear() + 1900;
+ int month = time.getMonth() + 1;
+ if (year < 1980) {
+ return DOS_TIME_MIN;
+ }
+ long value = ((year - 1980) << 25)
+ | (month << 21)
+ | (time.getDate() << 16)
+ | (time.getHours() << 11)
+ | (time.getMinutes() << 5)
+ | (time.getSeconds() >> 1);
+
+ byte[] result = new byte[4];
+ result[0] = (byte) ((value & 0xFF));
+ result[1] = (byte) ((value & 0xFF00) >> 8);
+ result[2] = (byte) ((value & 0xFF0000) >> 16);
+ result[3] = (byte) ((value & 0xFF000000l) >> 24);
+ return new ZipLong(result);
+ }
+
+}
diff --git a/src/main/org/apache/tools/zip/ZipShort.java b/src/main/org/apache/tools/zip/ZipShort.java
new file mode 100644
index 000000000..b4abf9fec
--- /dev/null
+++ b/src/main/org/apache/tools/zip/ZipShort.java
@@ -0,0 +1,138 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2001 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowlegement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Ant", and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ */
+
+package org.apache.tools.zip;
+
+/**
+ * Utility class that represents a two byte integer with conversion
+ * rules for the big endian byte order of ZIP files.
+ *
+ * @author Stefan Bodewig
+ * @version $Revision$
+ */
+public class ZipShort implements Cloneable {
+
+ private int value;
+
+ /**
+ * Create instance from a number.
+ *
+ * @since 1.1
+ */
+ public ZipShort (int value) {
+ this.value = value;
+ }
+
+ /**
+ * Create instance from bytes.
+ *
+ * @since 1.1
+ */
+ public ZipShort (byte[] bytes) {
+ this(bytes, 0);
+ }
+
+ /**
+ * Create instance from the two bytes starting at offset.
+ *
+ * @since 1.1
+ */
+ public ZipShort (byte[] bytes, int offset) {
+ value = (bytes[offset+1] << 8) & 0xFF00;
+ value += (bytes[offset] & 0xFF);
+ }
+
+ /**
+ * Get value as two bytes in big endian byte order.
+ *
+ * @since 1.1
+ */
+ public byte[] getBytes() {
+ byte[] result = new byte[2];
+ result[0] = (byte) (value & 0xFF);
+ result[1] = (byte) ((value & 0xFF00) >> 8);
+ return result;
+ }
+
+ /**
+ * Get value as Java int.
+ *
+ * @since 1.1
+ */
+ public int getValue() {
+ return value;
+ }
+
+ /**
+ * Override to make two instances with same value equal.
+ *
+ * @since 1.1
+ */
+ public boolean equals(Object o) {
+ if (o == null || !(o instanceof ZipShort)) {
+ return false;
+ }
+ return value == ((ZipShort) o).getValue();
+ }
+
+ /**
+ * Override to make two instances with same value equal.
+ *
+ * @since 1.1
+ */
+ public int hashCode() {
+ return value;
+ }
+
+}// ZipShort
diff --git a/src/testcases/org/apache/tools/zip/AsiExtraFieldTest.java b/src/testcases/org/apache/tools/zip/AsiExtraFieldTest.java
new file mode 100644
index 000000000..5c09c7cb3
--- /dev/null
+++ b/src/testcases/org/apache/tools/zip/AsiExtraFieldTest.java
@@ -0,0 +1,178 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2001 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowlegement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Ant", and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ */
+
+package org.apache.tools.zip;
+
+import junit.framework.TestCase;
+
+/**
+ * JUnit 3 testcases for org.apache.tools.zip.AsiExtraField.
+ *
+ * @author Stefan Bodewig
+ */
+public class AsiExtraFieldTest extends TestCase implements UnixStat {
+ public AsiExtraFieldTest(String name) {
+ super(name);
+ }
+
+ /**
+ * Test file mode magic.
+ */
+ public void testModes() {
+ AsiExtraField a = new AsiExtraField();
+ a.setMode(0123);
+ assertEquals("plain file", 0100123, a.getMode());
+ a.setDirectory(true);
+ assertEquals("directory", 040123, a.getMode());
+ a.setLinkedFile("test");
+ assertEquals("symbolic link", 0120123, a.getMode());
+ }
+
+ /**
+ * Test content.
+ */
+ public void testContent() {
+ AsiExtraField a = new AsiExtraField();
+ a.setMode(0123);
+ a.setUserId(5);
+ a.setGroupId(6);
+ byte[] b = a.getLocalFileDataData();
+
+ // CRC manually calculated, sorry
+ byte[] expect = {(byte)0xC6, 0x02, 0x78, (byte)0xB6, // CRC
+ 0123, (byte)0x80, // mode
+ 0, 0, 0, 0, // link length
+ 5, 0, 6, 0}; // uid, gid
+ assertEquals("no link", expect.length, b.length);
+ for (int i=0; i.
+ */
+
+package org.apache.tools.zip;
+
+import junit.framework.TestCase;
+
+/**
+ * JUnit 3 testcases for org.apache.tools.zip.ExtraFieldUtils.
+ *
+ * @author Stefan Bodewig
+ */
+public class ExtraFieldUtilsTest extends TestCase implements UnixStat {
+ public ExtraFieldUtilsTest(String name) {
+ super(name);
+ }
+
+ private AsiExtraField a;
+ private UnrecognizedExtraField dummy;
+ private byte[] data;
+ private byte[] aLocal;
+
+ public void setUp() {
+ a = new AsiExtraField();
+ a.setMode(0755);
+ a.setDirectory(true);
+ dummy = new UnrecognizedExtraField();
+ dummy.setHeaderId(new ZipShort(1));
+ dummy.setLocalFileDataData(new byte[0]);
+ dummy.setCentralDirectoryData(new byte[] {0});
+
+ aLocal = a.getLocalFileDataData();
+ byte[] dummyLocal = dummy.getLocalFileDataData();
+ data = new byte[4 + aLocal.length + 4 + dummyLocal.length];
+ System.arraycopy(a.getHeaderId().getBytes(), 0, data, 0, 2);
+ System.arraycopy(a.getLocalFileDataLength().getBytes(), 0, data, 2, 2);
+ System.arraycopy(aLocal, 0, data, 4, aLocal.length);
+ System.arraycopy(dummy.getHeaderId().getBytes(), 0, data,
+ 4+aLocal.length, 2);
+ System.arraycopy(dummy.getLocalFileDataLength().getBytes(), 0, data,
+ 4+aLocal.length+2, 2);
+ System.arraycopy(dummyLocal, 0, data,
+ 4+aLocal.length+4, dummyLocal.length);
+
+ }
+
+ /**
+ * test parser.
+ */
+ public void testParse() throws Exception {
+ ZipExtraField[] ze = ExtraFieldUtils.parse(data);
+ assertEquals("number of fields", 2, ze.length);
+ assert("type field 1", ze[0] instanceof AsiExtraField);
+ assertEquals("mode field 1", 040755,
+ ((AsiExtraField) ze[0]).getMode());
+ assert("type field 2", ze[1] instanceof UnrecognizedExtraField);
+ assertEquals("data length field 2", 0,
+ ze[1].getLocalFileDataLength().getValue());
+
+ byte[] data2 = new byte[data.length-1];
+ System.arraycopy(data, 0, data2, 0, data2.length);
+ try {
+ ExtraFieldUtils.parse(data2);
+ fail("data should be invalid");
+ } catch (Exception e) {
+ assertEquals("message",
+ "data starting at "+(4+aLocal.length)+" is in unknown format",
+ e.getMessage());
+ }
+ }
+
+ /**
+ * Test merge methods
+ */
+ public void testMerge() {
+ byte[] local =
+ ExtraFieldUtils.mergeLocalFileDataData(new ZipExtraField[] {a, dummy});
+ assertEquals("local length", data.length, local.length);
+ for (int i=0; i.
+ */
+
+package org.apache.tools.zip;
+
+import junit.framework.TestCase;
+
+/**
+ * JUnit 3 testcases for org.apache.tools.zip.ZipEntry.
+ *
+ * @author Stefan Bodewig
+ */
+public class ZipEntryTest extends TestCase {
+
+ public ZipEntryTest(String name) {
+ super(name);
+ }
+
+ /**
+ * test handling of extra fields
+ *
+ * @since 1.1
+ */
+ public void testExtraFields() {
+ AsiExtraField a = new AsiExtraField();
+ a.setDirectory(true);
+ a.setMode(0755);
+ UnrecognizedExtraField u = new UnrecognizedExtraField();
+ u.setHeaderId(new ZipShort(1));
+ u.setLocalFileDataData(new byte[0]);
+
+ ZipEntry ze = new ZipEntry("test/");
+ ze.setExtraFields(new ZipExtraField[] {a, u});
+ byte[] data1 = ze.getExtra();
+ ZipExtraField[] result = ze.getExtraFields();
+ assertEquals("first pass", 2, result.length);
+ assertSame(a, result[0]);
+ assertSame(u, result[1]);
+
+ UnrecognizedExtraField u2 = new UnrecognizedExtraField();
+ u2.setHeaderId(new ZipShort(1));
+ u2.setLocalFileDataData(new byte[] {1});
+
+ ze.addExtraField(u2);
+ byte[] data2 = ze.getExtra();
+ result = ze.getExtraFields();
+ assertEquals("second pass", 2, result.length);
+ assertSame(a, result[0]);
+ assertSame(u2, result[1]);
+ assertEquals("length second pass", data1.length+1, data2.length);
+
+ UnrecognizedExtraField u3 = new UnrecognizedExtraField();
+ u3.setHeaderId(new ZipShort(2));
+ u3.setLocalFileDataData(new byte[] {1});
+ ze.addExtraField(u3);
+ result = ze.getExtraFields();
+ assertEquals("third pass", 3, result.length);
+
+ ze.removeExtraField(new ZipShort(1));
+ byte[] data3 = ze.getExtra();
+ result = ze.getExtraFields();
+ assertEquals("fourth pass", 2, result.length);
+ assertSame(a, result[0]);
+ assertSame(u3, result[1]);
+ assertEquals("length fourth pass", data2.length, data3.length);
+
+ try {
+ ze.removeExtraField(new ZipShort(1));
+ fail("should be no such element");
+ } catch (java.util.NoSuchElementException nse) {
+ }
+ }
+}
diff --git a/src/testcases/org/apache/tools/zip/ZipLongTest.java b/src/testcases/org/apache/tools/zip/ZipLongTest.java
new file mode 100644
index 000000000..0c72db8a5
--- /dev/null
+++ b/src/testcases/org/apache/tools/zip/ZipLongTest.java
@@ -0,0 +1,119 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2001 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowlegement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Ant", and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ */
+
+package org.apache.tools.zip;
+
+import junit.framework.TestCase;
+
+/**
+ * JUnit 3 testcases for org.apache.tools.zip.ZipLong.
+ *
+ * @author Stefan Bodewig
+ */
+public class ZipLongTest extends TestCase {
+
+ public ZipLongTest(String name) {
+ super(name);
+ }
+
+ /**
+ * Test conversion to bytes.
+ */
+ public void testToBytes() {
+ ZipLong zl = new ZipLong(0x12345678);
+ byte[] result = zl.getBytes();
+ assertEquals("length getBytes", 4, result.length);
+ assertEquals("first byte getBytes", 0x78, result[0]);
+ assertEquals("second byte getBytes", 0x56, result[1]);
+ assertEquals("third byte getBytes", 0x34, result[2]);
+ assertEquals("fourth byte getBytes", 0x12, result[3]);
+ }
+
+ /**
+ * Test conversion from bytes.
+ */
+ public void testFromBytes() {
+ byte[] val = new byte[] {0x78, 0x56, 0x34, 0x12};
+ ZipLong zl = new ZipLong(val);
+ assertEquals("value from bytes", 0x12345678, zl.getValue());
+ }
+
+ /**
+ * Test the contract of the equals method.
+ */
+ public void testEquals() {
+ ZipLong zl = new ZipLong(0x12345678);
+ ZipLong zl2 = new ZipLong(0x12345678);
+ ZipLong zl3 = new ZipLong(0x87654321);
+
+ assert("reflexive", zl.equals(zl));
+
+ assert("works", zl.equals(zl2));
+ assert("works, part two", !zl.equals(zl3));
+
+ assert("symmetric", zl2.equals(zl));
+
+ assert("null handling", !zl.equals(null));
+ assert("non ZipLong handling", !zl.equals(new Integer(0x1234)));
+ }
+
+ /**
+ * Test sign handling.
+ */
+ public void testSign() {
+ ZipLong zl = new ZipLong(new byte[] {(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF});
+ assertEquals(0x00000000FFFFFFFFl, zl.getValue());
+ }
+
+}
diff --git a/src/testcases/org/apache/tools/zip/ZipShortTest.java b/src/testcases/org/apache/tools/zip/ZipShortTest.java
new file mode 100644
index 000000000..733f3f867
--- /dev/null
+++ b/src/testcases/org/apache/tools/zip/ZipShortTest.java
@@ -0,0 +1,117 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2001 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowlegement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Ant", and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ */
+
+package org.apache.tools.zip;
+
+import junit.framework.TestCase;
+
+/**
+ * JUnit 3 testcases for org.apache.tools.zip.ZipShort.
+ *
+ * @author Stefan Bodewig
+ */
+public class ZipShortTest extends TestCase {
+
+ public ZipShortTest(String name) {
+ super(name);
+ }
+
+ /**
+ * Test conversion to bytes.
+ */
+ public void testToBytes() {
+ ZipShort zs = new ZipShort(0x1234);
+ byte[] result = zs.getBytes();
+ assertEquals("length getBytes", 2, result.length);
+ assertEquals("first byte getBytes", 0x34, result[0]);
+ assertEquals("second byte getBytes", 0x12, result[1]);
+ }
+
+ /**
+ * Test conversion from bytes.
+ */
+ public void testFromBytes() {
+ byte[] val = new byte[] {0x34, 0x12};
+ ZipShort zs = new ZipShort(val);
+ assertEquals("value from bytes", 0x1234, zs.getValue());
+ }
+
+ /**
+ * Test the contract of the equals method.
+ */
+ public void testEquals() {
+ ZipShort zs = new ZipShort(0x1234);
+ ZipShort zs2 = new ZipShort(0x1234);
+ ZipShort zs3 = new ZipShort(0x5678);
+
+ assert("reflexive", zs.equals(zs));
+
+ assert("works", zs.equals(zs2));
+ assert("works, part two", !zs.equals(zs3));
+
+ assert("symmetric", zs2.equals(zs));
+
+ assert("null handling", !zs.equals(null));
+ assert("non ZipShort handling", !zs.equals(new Integer(0x1234)));
+ }
+
+ /**
+ * Test sign handling.
+ */
+ public void testSign() {
+ ZipShort zs = new ZipShort(new byte[] {(byte)0xFF, (byte)0xFF});
+ assertEquals(0x0000FFFF, zs.getValue());
+ }
+
+}