Author: bodewig
Date: Mon Aug  8 08:54:33 2011
New Revision: 1154877

URL: http://svn.apache.org/viewvc?rev=1154877&view=rev
Log:
support reading of long file names in AR archives that use the BSD variant.  
COMPRESS-144

Added:
    commons/proper/compress/trunk/src/test/resources/longfile_bsd.ar
      - copied, changed from r1154851, 
commons/proper/compress/trunk/src/test/resources/longfile_gnu.ar
Modified:
    
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStream.java
    commons/proper/compress/trunk/src/site/xdoc/examples.xml
    
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStreamTest.java

Modified: 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStream.java
URL: 
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStream.java?rev=1154877&r1=1154876&r2=1154877&view=diff
==============================================================================
--- 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStream.java
 (original)
+++ 
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStream.java
 Mon Aug  8 08:54:33 2011
@@ -18,6 +18,7 @@
  */
 package org.apache.commons.compress.archivers.ar;
 
+import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
 
@@ -143,6 +144,7 @@ public class ArArchiveInputStream extend
 
         // entry name is stored as ASCII string
         String temp = ArchiveUtils.toAsciiString(name).trim();
+        long len = asLong(length);
 
         if (temp.equals("//")){ // GNU extended filenames entry
             int bufflen = asInt(length); // Assume length will fit in an int
@@ -158,8 +160,17 @@ public class ArArchiveInputStream extend
         } else if (temp.matches("^/\\d+")) {// GNU long filename ref.
             int offset = Integer.parseInt(temp.substring(1));// get the offset
             temp = getExtendedName(offset); // convert to the long name
+        } else if (isBSDLongName(temp)) {
+            temp = getBSDLongName(temp);
+            // entry length contained the length of the file name in
+            // addition to the real length of the entry.
+            // assume file name was ASCII, there is no "standard" otherwise
+            int nameLen = temp.length();
+            len -= nameLen;
+            entryOffset += nameLen;
         }
-        currentEntry = new ArArchiveEntry(temp, asLong(length), asInt(userid, 
true),
+
+        currentEntry = new ArArchiveEntry(temp, len, asInt(userid, true),
                                           asInt(groupid, true), 
asInt(filemode, 8),
                                           asLong(lastmodified));
         return currentEntry;
@@ -301,4 +312,58 @@ public class ArArchiveInputStream extend
         return true;
     }
 
+    private static final String BSD_LONGNAME_PREFIX = "#1/";
+    private static final int BSD_LONGNAME_PREFIX_LEN =
+        BSD_LONGNAME_PREFIX.length();
+
+    /**
+     * Does the name look like it is a long name (or a name containing
+     * spaces) as encoded by BSD ar?
+     *
+     * <p>From the FreeBSD ar(5) man page:</p>
+     * <pre>
+     * BSD   In the BSD variant, names that are shorter than 16
+     *      characters and without embedded spaces are stored
+     *      directly in this field.  If a name has an embedded
+     *      space, or if it is longer than 16 characters, then
+     *      the string "#1/" followed by the decimal represen-
+     *      tation of the length of the file name is placed in
+     *      this field.        The actual file name is stored immedi-
+     *      ately after the archive header.  The content of the
+     *      archive member follows the file name.  The ar_size
+     *      field of the header (see below) will then hold the
+     *      sum of the size of the file name and the size of
+     *      the member.
+     * </pre>
+     *
+     * @since Apache Commons Compress 1.3
+     */
+    private static boolean isBSDLongName(String name) {
+        return name.startsWith(BSD_LONGNAME_PREFIX)
+            && name.length() > BSD_LONGNAME_PREFIX_LEN;
+    }
+
+    /**
+     * Reads the real name from the current stream assuming the very
+     * first bytes to be read are the real file name.
+     *
+     * @see #isBSDLongName
+     */
+    private String getBSDLongName(String bsdLongName) throws IOException {
+        int nameLen =
+            Integer.parseInt(bsdLongName.substring(BSD_LONGNAME_PREFIX_LEN));
+        byte[] name = new byte[nameLen];
+        int read = 0, readNow = 0;
+        while ((readNow = input.read(name, read, nameLen - read)) >= 0) {
+            read += readNow;
+            count(readNow);
+            if (read == nameLen) {
+                break;
+            }
+        }
+        if (read != nameLen) {
+            throw new EOFException();
+        }
+        return ArchiveUtils.toAsciiString(name);
+    }
 }

Modified: commons/proper/compress/trunk/src/site/xdoc/examples.xml
URL: 
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/site/xdoc/examples.xml?rev=1154877&r1=1154876&r2=1154877&view=diff
==============================================================================
--- commons/proper/compress/trunk/src/site/xdoc/examples.xml (original)
+++ commons/proper/compress/trunk/src/site/xdoc/examples.xml Mon Aug  8 
08:54:33 2011
@@ -98,10 +98,11 @@ LOOP UNTIL entry.getSize() HAS BEEN READ
         <p>Traditionally the AR format doesn't allow file names longer
           than 16 characters.  There are two variants that circumvent
           this limitation in different ways, the GNU/SRV4 and the BSD
-          variant.  Currently Commons Compress can only read archives
-          using the GNU/SRV4 variant, it doesn't support writing
-          archives with file names longer than 16 characters at
-          all.</p>
+          variant.  Commons Compress 1.0 to 1.2 can only read archives
+          using the GNU/SRV4 variant, support for the BSD variant has
+          been added in Commons Compress 1.3.  It doesn't support
+          writing archives with file names longer than 16 characters
+          at all.</p>
 
       </subsection>
 

Modified: 
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStreamTest.java
URL: 
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStreamTest.java?rev=1154877&r1=1154876&r2=1154877&view=diff
==============================================================================
--- 
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStreamTest.java
 (original)
+++ 
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/ar/ArArchiveInputStreamTest.java
 Mon Aug  8 08:54:33 2011
@@ -18,15 +18,39 @@
 
 package org.apache.commons.compress.archivers.ar;
 
-import java.util.ArrayList;
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
 import org.apache.commons.compress.AbstractTestCase;
+import org.apache.commons.compress.archivers.ArchiveEntry;
+import org.apache.commons.compress.utils.ArchiveUtils;
 
 public class ArArchiveInputStreamTest extends AbstractTestCase {
 
     public void testReadLongNamesGNU() throws Exception {
-        ArrayList<String> l = new ArrayList<String>();
-        l.add("this_is_a_long_file_name.txt");
-        checkArchiveContent(getFile("longfile_gnu.ar"), l);
+        checkLongNameEntry("longfile_gnu.ar");
     }
 
+    public void testReadLongNamesBSD() throws Exception {
+        checkLongNameEntry("longfile_bsd.ar");
+    }
+
+    private void checkLongNameEntry(String archive) throws Exception {
+        FileInputStream fis = new FileInputStream(getFile(archive));
+        ArArchiveInputStream s = null;
+        try {
+            s = new ArArchiveInputStream(new BufferedInputStream(fis));
+            ArchiveEntry e = s.getNextEntry();
+            assertEquals("this_is_a_long_file_name.txt", e.getName());
+            assertEquals(14, e.getSize());
+            byte[] hello = new byte[14];
+            s.read(hello);
+            assertEquals("Hello, world!\n", ArchiveUtils.toAsciiString(hello));
+            assertNull(s.getNextEntry());
+        } finally {
+            if (s != null) {
+                s.close();
+            }
+            fis.close();
+        }
+    }
 }

Copied: commons/proper/compress/trunk/src/test/resources/longfile_bsd.ar (from 
r1154851, commons/proper/compress/trunk/src/test/resources/longfile_gnu.ar)
URL: 
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/resources/longfile_bsd.ar?p2=commons/proper/compress/trunk/src/test/resources/longfile_bsd.ar&p1=commons/proper/compress/trunk/src/test/resources/longfile_gnu.ar&r1=1154851&r2=1154877&rev=1154877&view=diff
==============================================================================
Binary files - no diff available.


Reply via email to