commit a30fd10825b09aa529a025cd7d31e8d8ffb17637
Author: Stefan Bodewig <bodewig@apache.org>
Date:   Thu Feb 19 17:19:23 2009 +0000

    Support more modern encoding flag where archives signal filenames as UTF-8.  Based on submissions by Wolfgang Glas to commons-compress and TAMURA Kent to Ant.  PR 45548.
    
    git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@745930 13f79535-47bb-0310-9956-ffa450edef68

Index: apache-ant-1.7.1/WHATSNEW
===================================================================
--- apache-ant-1.7.1.orig/WHATSNEW	2014-05-27 20:59:55.802450426 +0200
+++ apache-ant-1.7.1/WHATSNEW	2014-05-27 20:59:59.779473012 +0200
@@ -141,6 +141,11 @@
   subclases should now work again - without any changes to the
   subclass.
 
+ * The zip package detects the encoding bit set by more modern
+   archivers when they write UTF-8 filenames and optionally sets it
+   when writing zips or jars.
+   Bugzilla Report 45548
+
 Other changes:
 --------------
 
Index: apache-ant-1.7.1/src/main/org/apache/tools/zip/ZipFile.java
===================================================================
--- apache-ant-1.7.1.orig/src/main/org/apache/tools/zip/ZipFile.java	2014-05-27 20:59:55.802450426 +0200
+++ apache-ant-1.7.1/src/main/org/apache/tools/zip/ZipFile.java	2014-05-27 20:59:59.779473012 +0200
@@ -285,7 +285,15 @@
             off += SHORT;
             ze.setPlatform((versionMadeBy >> BYTE_SHIFT) & NIBLET_MASK);
 
-            off += WORD; // skip version info and general purpose byte
+            off += SHORT; // skip version info
+
+            final int generalPurposeFlag = ZipShort.getValue(cfh, off);
+            final String entryEncoding = 
+                (generalPurposeFlag & ZipOutputStream.EFS_FLAG) != 0
+                ? ZipOutputStream.UTF8
+                : encoding;
+
+            off += SHORT;
 
             ze.setMethod(ZipShort.getValue(cfh, off));
             off += SHORT;
@@ -325,8 +333,7 @@
 
             byte[] fileName = new byte[fileNameLen];
             archive.readFully(fileName);
-            ze.setName(getString(fileName));
-
+            ze.setName(getString(fileName, entryEncoding));
 
             // LFH offset,
             OffsetEntry offset = new OffsetEntry();
@@ -340,7 +347,7 @@
 
             byte[] comment = new byte[commentLen];
             archive.readFully(comment);
-            ze.setComment(getString(comment));
+            ze.setComment(getString(comment, entryEncoding));
 
             archive.readFully(signatureBytes);
             sig = ZipLong.getValue(signatureBytes);
@@ -498,11 +505,24 @@
      * @throws ZipException if the encoding cannot be recognized.
      */
     protected String getString(byte[] bytes) throws ZipException {
-        if (encoding == null) {
+        return getString(bytes, encoding);
+    }
+
+    /**
+     * Retrieve a String from the given bytes using the encoding set
+     * for this ZipFile.
+     *
+     * @param bytes the byte array to transform
+     * @return String obtained by using the given encoding
+     * @throws ZipException if the encoding cannot be recognized.
+     */
+    protected String getString(byte[] bytes, String enc)
+        throws ZipException {
+        if (enc == null) {
             return new String(bytes);
         } else {
             try {
-                return new String(bytes, encoding);
+                return new String(bytes, enc);
             } catch (UnsupportedEncodingException uee) {
                 throw new ZipException(uee.getMessage());
             }
Index: apache-ant-1.7.1/src/main/org/apache/tools/zip/ZipOutputStream.java
===================================================================
--- apache-ant-1.7.1.orig/src/main/org/apache/tools/zip/ZipOutputStream.java	2014-05-27 20:59:55.803450432 +0200
+++ apache-ant-1.7.1/src/main/org/apache/tools/zip/ZipOutputStream.java	2014-05-27 20:59:59.779473012 +0200
@@ -81,6 +81,17 @@
     public static final int STORED = java.util.zip.ZipEntry.STORED;
 
     /**
+     * name of the encoding UTF-8
+     */
+    static final String UTF8 = "UTF8";
+
+     /**
+     * General purpose flag, which indicates that filenames are
+     * written in utf-8.
+     */
+    public static final int EFS_FLAG = 1 << 11;
+
+    /**
      * Current entry.
      *
      * @since 1.1
@@ -234,6 +245,11 @@
     private RandomAccessFile raf = null;
 
     /**
+     * whether to use the EFS flag when writing UTF-8 filenames or not.
+     */
+    private boolean useEFS = true; 
+
+    /**
      * Creates a new ZIP OutputStream filtering the underlying stream.
      * @param out the outputstream to zip
      * @since 1.1
@@ -291,8 +307,9 @@
      * @param encoding the encoding value
      * @since 1.3
      */
-    public void setEncoding(String encoding) {
+    public void setEncoding(final String encoding) {
         this.encoding = encoding;
+        useEFS &= isUTF8(encoding);
     }
 
     /**
@@ -307,6 +324,15 @@
     }
 
     /**
+     * Whether to set the EFS flag if the file name encoding is UTF-8.
+     *
+     * <p>Defaults to true.</p>
+     */
+    public void setUseEFS(boolean b) {
+        useEFS = b && isUTF8(encoding);
+    }
+
+    /**
      * Finishs writing the contents and closes this as well as the
      * underlying stream.
      *
@@ -597,21 +623,7 @@
         //store method in local variable to prevent multiple method calls
         final int zipMethod = ze.getMethod();
 
-        // version needed to extract
-        // general purpose bit flag
-        // CheckStyle:MagicNumber OFF
-        if (zipMethod == DEFLATED && raf == null) {
-            // requires version 2 as we are going to store length info
-            // in the data descriptor
-            writeOut(ZipShort.getBytes(20));
-
-            // bit3 set to signal, we use a data descriptor
-            writeOut(ZipShort.getBytes(8));
-        } else {
-            writeOut(ZipShort.getBytes(10));
-            writeOut(ZERO);
-        }
-        // CheckStyle:MagicNumber ON
+        writeVersionNeededToExtractAndGeneralPurposeBits(zipMethod);
         written += WORD;
 
         // compression method
@@ -696,24 +708,12 @@
         writeOut(ZipShort.getBytes((ze.getPlatform() << 8) | 20));
         written += SHORT;
 
-        // version needed to extract
-        // general purpose bit flag
-        if (ze.getMethod() == DEFLATED && raf == null) {
-            // requires version 2 as we are going to store length info
-            // in the data descriptor
-            writeOut(ZipShort.getBytes(20));
-
-            // bit3 set to signal, we use a data descriptor
-            writeOut(ZipShort.getBytes(8));
-        } else {
-            writeOut(ZipShort.getBytes(10));
-            writeOut(ZERO);
-        }
-        // CheckStyle:MagicNumber ON
+        final int zipMethod = ze.getMethod();
+        writeVersionNeededToExtractAndGeneralPurposeBits(zipMethod);
         written += WORD;
 
         // compression method
-        writeOut(ZipShort.getBytes(ze.getMethod()));
+        writeOut(ZipShort.getBytes(zipMethod));
         written += SHORT;
 
         // last mod. time and date
@@ -915,4 +921,38 @@
         }
     }
 
+    /**
+     * Whether a given encoding - or the platform's default encoding
+     * if the parameter is null - is UTF-8.
+     */
+    static boolean isUTF8(String encoding) {
+        if (encoding == null) {
+            // check platform's default encoding
+            encoding = System.getProperty("file.encoding");
+        }
+        return UTF8.equalsIgnoreCase(encoding)
+            || "utf-8".equalsIgnoreCase(encoding);
+    }
+
+    private void writeVersionNeededToExtractAndGeneralPurposeBits(final int
+                                                                  zipMethod)
+        throws IOException {
+
+        // CheckStyle:MagicNumber OFF
+        int versionNeededToExtract = 10;
+        int generalPurposeFlag = useEFS ? EFS_FLAG : 0;
+        if (zipMethod == DEFLATED && raf == null) {
+            // requires version 2 as we are going to store length info
+            // in the data descriptor
+            versionNeededToExtract =  20;
+            // bit3 set to signal, we use a data descriptor
+            generalPurposeFlag |= 8;
+        }
+        // CheckStyle:MagicNumber ON
+
+        // version needed to extract
+        writeOut(ZipShort.getBytes(versionNeededToExtract));
+        // general purpose bit flag
+        writeOut(ZipShort.getBytes(generalPurposeFlag));
+    }
 }
