[prev in list] [next in list] [prev in thread] [next in thread] 

List:       jakarta-commons-dev
Subject:    svn commit: r427072 [2/5] - in /jakarta/commons/sandbox/compress/trunk: ./
From:       tcurdt () apache ! org
Date:       2006-07-31 10:55:13
Message-ID: 20060731105521.C5EDB1A981F () eris ! apache ! org
[Download RAW message or body]

Added: jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/tar/TarEntry.java
                
URL: http://svn.apache.org/viewvc/jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/tar/TarEntry.java?rev=427072&view=auto
 ==============================================================================
--- jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/tar/TarEntry.java \
                (added)
+++ jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/tar/TarEntry.java \
Mon Jul 31 03:55:10 2006 @@ -0,0 +1,811 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.compress.archivers.tar;
+
+import java.io.File;
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ * This class represents an entry in a Tar archive. It consists of the entry's
+ * header, as well as the entry's File. Entries can be instantiated in one of
+ * three ways, depending on how they are to be used. <p>
+ *
+ * TarEntries that are created from the header bytes read from an archive are
+ * instantiated with the TarEntry( byte[] ) constructor. These entries will be
+ * used when extracting from or listing the contents of an archive. These
+ * entries have their header filled in using the header bytes. They also set the
+ * File to null, since they reference an archive entry not a file. <p>
+ *
+ * TarEntries that are created from Files that are to be written into an archive
+ * are instantiated with the TarEntry( File ) constructor. These entries have
+ * their header filled in using the File's information. They also keep a
+ * reference to the File for convenience when writing entries. <p>
+ *
+ * Finally, TarEntries can be constructed from nothing but a name. This allows
+ * the programmer to construct the entry by hand, for instance when only an
+ * InputStream is available for writing to the archive, and the header
+ * information is constructed from other information. In this case the header
+ * fields are set to defaults and the File is set to null. <p>
+ *
+ * The C structure for a Tar Entry's header is: <pre>
+ * struct header {
+ * char name[NAMSIZ];
+ * char mode[8];
+ * char uid[8];
+ * char gid[8];
+ * char size[12];
+ * char mtime[12];
+ * char chksum[8];
+ * char linkflag;
+ * char linkname[NAMSIZ];
+ * char magic[8];
+ * char uname[TUNMLEN];
+ * char gname[TGNMLEN];
+ * char devmajor[8];
+ * char devminor[8];
+ * } header;
+ * </pre>
+ *
+ * @author <a href="mailto:time@ice.com">Timothy Gerard Endres</a>
+ * @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a>
+ * @author <a href="mailto:peter@apache.org">Peter Donald</a>
+ * @version $Revision: 155439 $ $Date$
+ * @see TarInputStream
+ * @see TarOutputStream
+ */
+public class TarEntry
+{
+	/**
+     * The length of the mode field in a header buffer.
+     */
+	private final static int MODELEN = 8;
+
+    /**
+     * The length of the user id field in a header buffer.
+     */
+     private final static int UIDLEN = 8;
+
+    /**
+     * The length of the group id field in a header buffer.
+     */
+    private final static int GIDLEN = 8;
+
+    /**
+     * The length of the checksum field in a header buffer.
+     */
+    private final static int CHKSUMLEN = 8;
+
+    /**
+     * The length of the size field in a header buffer.
+     */
+    private final static int SIZELEN = 12;
+
+    /**
+     * The length of the magic field in a header buffer.
+     */
+    private final static int MAGICLEN = 8;
+
+    /**
+     * The length of the modification time field in a header buffer.
+     */
+    private final static int MODTIMELEN = 12;
+
+    /**
+     * The length of the user name field in a header buffer.
+     */
+    private final static int UNAMELEN = 32;
+
+    /**
+     * The length of the group name field in a header buffer.
+     */
+    private final static int GNAMELEN = 32;
+
+    /**
+     * The length of the devices field in a header buffer.
+     */
+    private final static int DEVLEN = 8;
+
+    /**
+     * LF_ constants represent the "link flag" of an entry, or more commonly,
+     * the "entry type". This is the "old way" of indicating a normal file.
+     */
+    private final static byte LF_OLDNORM = 0;
+
+    /**
+     * Normal file type.
+     */
+    private final static byte LF_NORMAL = (byte)'0';
+
+    /**
+     * Link file type.
+     */
+    private final static byte LF_LINK = (byte)'1';
+
+    /**
+     * Symbolic link file type.
+     */
+    private final static byte LF_SYMLINK = (byte)'2';
+
+    /**
+     * Character device file type.
+     */
+    private final static byte LF_CHR = (byte)'3';
+
+    /**
+     * Block device file type.
+     */
+    private final static byte LF_BLK = (byte)'4';
+
+    /**
+     * Directory file type.
+     */
+    private final static byte LF_DIR = (byte)'5';
+
+    /**
+     * FIFO (pipe) file type.
+     */
+    private final static byte LF_FIFO = (byte)'6';
+
+    /**
+     * Contiguous file type.
+     */
+    private final static byte LF_CONTIG = (byte)'7';
+
+    /**
+     * The magic tag representing a POSIX tar archive.
+     */
+    private final static String TMAGIC = "ustar";
+
+    /**
+     * The magic tag representing a GNU tar archive.
+     */
+    private final static String GNU_TMAGIC = "ustar  ";
+
+    /**
+     * The name of the GNU tar entry which contains a long name.
+     */
+    static String GNU_LONGLINK = "././@LongLink";
+
+    /**
+     * Identifies the *next* file on the tape as having a long name.
+     */
+    static byte LF_GNUTYPE_LONGNAME = (byte)'L';
+    
+    
+    
+    
+    
+    
+    
+    
+    /**
+     * The length of the name field in a header buffer.
+     */
+    public static final int NAMELEN = 100;
+
+    /**
+     * The entry's modification time.
+     */
+    private int m_checkSum;
+
+    /**
+     * The entry's group name.
+     */
+    private int m_devMajor;
+
+    /**
+     * The entry's major device number.
+     */
+    private int m_devMinor;
+
+    /**
+     * The entry's minor device number.
+     */
+    private File m_file;
+
+    /**
+     * The entry's user id.
+     */
+    private int m_groupID;
+
+    /**
+     * The entry's user name.
+     */
+    private StringBuffer m_groupName;
+
+    /**
+     * The entry's checksum.
+     */
+    private byte m_linkFlag;
+
+    /**
+     * The entry's link flag.
+     */
+    private StringBuffer m_linkName;
+
+    /**
+     * The entry's link name.
+     */
+    private StringBuffer m_magic;
+
+    /**
+     * The entry's size.
+     */
+    private long m_modTime;
+
+    /**
+     * The entry's name.
+     */
+    private int m_mode;
+
+    private StringBuffer m_name;
+
+    /**
+     * The entry's group id.
+     */
+    private long m_size;
+
+    /**
+     * The entry's permission mode.
+     */
+    private int m_userID;
+
+    /**
+     * The entry's magic tag.
+     */
+    private StringBuffer m_userName;
+
+    /**
+     * Construct an entry with only a name. This allows the programmer to
+     * construct the entry's header "by hand". File is set to null.
+     *
+     * @param name the name of the entry
+     */
+    public TarEntry( final String name )
+    {
+        this();
+
+        final boolean isDir = name.endsWith( "/" );
+
+        m_name = new StringBuffer( name );
+        m_mode = isDir ? 040755 : 0100644;
+        m_linkFlag = isDir ? TarEntry.LF_DIR : TarEntry.LF_NORMAL;
+        m_modTime = ( new Date() ).getTime() / 1000;
+        m_linkName = new StringBuffer( "" );
+        m_userName = new StringBuffer( "" );
+        m_groupName = new StringBuffer( "" );
+    }
+
+    /**
+     * Construct an entry with a name an a link flag.
+     *
+     * @param name Description of Parameter
+     * @param linkFlag Description of Parameter
+     */
+    public TarEntry( final String name, final byte linkFlag )
+    {
+        this( name );
+        m_linkFlag = linkFlag;
+    }
+
+    /**
+     * Construct an entry for a file. File is set to file, and the header is
+     * constructed from information from the file.
+     *
+     * @param file The file that the entry represents.
+     */
+    public TarEntry( final File file )
+    {
+        this();
+
+        m_file = file;
+
+        String name = file.getPath();
+
+        // Strip off drive letters!
+        final String osName =
+            System.getProperty( "os.name" ).toLowerCase( Locale.US );
+        if( -1 != osName.indexOf( "netware" ) )
+        {
+            if( name.length() > 2 )
+            {
+                final char ch1 = name.charAt( 0 );
+                final char ch2 = name.charAt( 1 );
+
+                if( ch2 == ':' &&
+                    ( ( ch1 >= 'a' && ch1 <= 'z' ) ||
+                    ( ch1 >= 'A' && ch1 <= 'Z' ) ) )
+                {
+                    name = name.substring( 2 );
+                }
+            }
+        }
+        else if( -1 != osName.indexOf( "netware" ) )
+        {
+            final int colon = name.indexOf( ':' );
+            if( colon != -1 )
+            {
+                name = name.substring( colon + 1 );
+            }
+        }
+
+        name = name.replace( File.separatorChar, '/' );
+
+        // No absolute pathnames
+        // Windows (and Posix?) paths can start with "\\NetworkDrive\",
+        // so we loop on starting /'s.
+        while( name.startsWith( "/" ) )
+        {
+            name = name.substring( 1 );
+        }
+
+        m_linkName = new StringBuffer( "" );
+        m_name = new StringBuffer( name );
+
+        if( file.isDirectory() )
+        {
+            m_mode = 040755;
+            m_linkFlag = TarEntry.LF_DIR;
+
+            if( m_name.charAt( m_name.length() - 1 ) != '/' )
+            {
+                m_name.append( "/" );
+            }
+        }
+        else
+        {
+            m_mode = 0100644;
+            m_linkFlag = TarEntry.LF_NORMAL;
+        }
+
+        m_size = file.length();
+        m_modTime = file.lastModified() / 1000;
+        m_checkSum = 0;
+        m_devMajor = 0;
+        m_devMinor = 0;
+    }
+
+    /**
+     * Construct an entry from an archive's header bytes. File is set to null.
+     *
+     * @param header The header bytes from a tar archive entry.
+     */
+    public TarEntry( final byte[] header )
+    {
+        this();
+        parseTarHeader( header );
+    }
+
+    /**
+     * Construct an empty entry and prepares the header values.
+     */
+    private TarEntry()
+    {
+        m_magic = new StringBuffer( TarEntry.TMAGIC );
+        m_name = new StringBuffer();
+        m_linkName = new StringBuffer();
+
+        String user = System.getProperty( "user.name", "" );
+        if( user.length() > 31 )
+        {
+            user = user.substring( 0, 31 );
+        }
+
+        m_userName = new StringBuffer( user );
+        m_groupName = new StringBuffer( "" );
+    }
+
+    /**
+     * Set this entry's group id.
+     *
+     * @param groupId This entry's new group id.
+     */
+    public void setGroupID( final int groupId )
+    {
+        m_groupID = groupId;
+    }
+
+    /**
+     * Set this entry's group id.
+     *
+     * @param groupId This entry's new group id.
+     * @deprecated Use setGroupID() instead
+     * @see #setGroupID(int)
+     */
+    public void setGroupId( final int groupId )
+    {
+        m_groupID = groupId;
+    }
+
+    /**
+     * Set this entry's group name.
+     *
+     * @param groupName This entry's new group name.
+     */
+    public void setGroupName( final String groupName )
+    {
+        m_groupName = new StringBuffer( groupName );
+    }
+
+    /**
+     * Set this entry's modification time. The parameter passed to this method
+     * is in "Java time".
+     *
+     * @param time This entry's new modification time.
+     */
+    public void setModTime( final long time )
+    {
+        m_modTime = time / 1000;
+    }
+
+    /**
+     * Set this entry's modification time.
+     *
+     * @param time This entry's new modification time.
+     */
+    public void setModTime( final Date time )
+    {
+        m_modTime = time.getTime() / 1000;
+    }
+
+    /**
+     * Set the mode for this entry
+     *
+     * @param mode The new Mode value
+     */
+    public void setMode( final int mode )
+    {
+        m_mode = mode;
+    }
+
+    /**
+     * Set this entry's name.
+     *
+     * @param name This entry's new name.
+     */
+    public void setName( final String name )
+    {
+        m_name = new StringBuffer( name );
+    }
+
+    /**
+     * Set this entry's file size.
+     *
+     * @param size This entry's new file size.
+     */
+    public void setSize( final long size )
+    {
+        m_size = size;
+    }
+
+    /**
+     * Set this entry's user id.
+     *
+     * @param userId This entry's new user id.
+     */
+    public void setUserID( final int userId )
+    {
+        m_userID = userId;
+    }
+
+    /**
+     * Set this entry's user id.
+     *
+     * @param userId This entry's new user id.
+     * @deprecated Use setUserID() instead
+     * @see #setUserID(int)
+     */
+    public void setUserId( final int userId )
+    {
+        m_userID = userId;
+    }
+
+    /**
+     * Set this entry's user name.
+     *
+     * @param userName This entry's new user name.
+     */
+    public void setUserName( final String userName )
+    {
+        m_userName = new StringBuffer( userName );
+    }
+
+    /**
+     * If this entry represents a file, and the file is a directory, return an
+     * array of TarEntries for this entry's children.
+     *
+     * @return An array of TarEntry's for this entry's children.
+     */
+    public TarEntry[] getDirectoryEntries()
+    {
+        if( null == m_file || !m_file.isDirectory() )
+        {
+            return new TarEntry[ 0 ];
+        }
+
+        final String[] list = m_file.list();
+        final TarEntry[] result = new TarEntry[ list.length ];
+
+        for( int i = 0; i < list.length; ++i )
+        {
+            result[ i ] = new TarEntry( new File( m_file, list[ i ] ) );
+        }
+
+        return result;
+    }
+
+    /**
+     * Get this entry's file.
+     *
+     * @return This entry's file.
+     */
+    public File getFile()
+    {
+        return m_file;
+    }
+
+    /**
+     * Get this entry's group id.
+     *
+     * @return This entry's group id.
+     * @deprecated Use getGroupID() instead
+     * @see #getGroupID()
+     */
+    public int getGroupId()
+    {
+        return m_groupID;
+    }
+
+    /**
+     * Get this entry's group id.
+     *
+     * @return This entry's group id.
+     */
+    public int getGroupID()
+    {
+        return m_groupID;
+    }
+
+    /**
+     * Get this entry's group name.
+     *
+     * @return This entry's group name.
+     */
+    public String getGroupName()
+    {
+        return m_groupName.toString();
+    }
+
+    /**
+     * Set this entry's modification time.
+     *
+     * @return The ModTime value
+     */
+    public Date getModTime()
+    {
+        return new Date( m_modTime * 1000 );
+    }
+
+    /**
+     * Get this entry's mode.
+     *
+     * @return This entry's mode.
+     */
+    public int getMode()
+    {
+        return m_mode;
+    }
+
+    /**
+     * Get this entry's name.
+     *
+     * @return This entry's name.
+     */
+    public String getName()
+    {
+        return m_name.toString();
+    }
+
+    /**
+     * Get this entry's file size.
+     *
+     * @return This entry's file size.
+     */
+    public long getSize()
+    {
+        return m_size;
+    }
+
+    /**
+     * Get this entry's checksum.
+     *
+     * @return This entry's checksum.
+     */
+    public int getCheckSum()
+    {
+        return m_checkSum;
+    }
+
+    /**
+     * Get this entry's user id.
+     *
+     * @return This entry's user id.
+     * @deprecated Use getUserID() instead
+     * @see #getUserID()
+     */
+    public int getUserId()
+    {
+        return m_userID;
+    }
+
+    /**
+     * Get this entry's user id.
+     *
+     * @return This entry's user id.
+     */
+    public int getUserID()
+    {
+        return m_userID;
+    }
+
+    /**
+     * Get this entry's user name.
+     *
+     * @return This entry's user name.
+     */
+    public String getUserName()
+    {
+        return m_userName.toString();
+    }
+
+    /**
+     * Determine if the given entry is a descendant of this entry. Descendancy
+     * is determined by the name of the descendant starting with this entry's
+     * name.
+     *
+     * @param desc Entry to be checked as a descendent of
+     * @return True if entry is a descendant of
+     */
+    public boolean isDescendent( final TarEntry desc )
+    {
+        return desc.getName().startsWith( getName() );
+    }
+
+    /**
+     * Return whether or not this entry represents a directory.
+     *
+     * @return True if this entry is a directory.
+     */
+    public boolean isDirectory()
+    {
+        if( m_file != null )
+        {
+            return m_file.isDirectory();
+        }
+
+        if( m_linkFlag == TarEntry.LF_DIR )
+        {
+            return true;
+        }
+
+        if( getName().endsWith( "/" ) )
+        {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Indicate if this entry is a GNU long name block
+     *
+     * @return true if this is a long name extension provided by GNU tar
+     */
+    public boolean isGNULongNameEntry()
+    {
+        return m_linkFlag == TarEntry.LF_GNUTYPE_LONGNAME &&
+            m_name.toString().equals( TarEntry.GNU_LONGLINK );
+    }
+
+    /**
+     * Determine if the two entries are equal. Equality is determined by the
+     * header names being equal.
+     *
+     * @param other Entry to be checked for equality.
+     * @return True if the entries are equal.
+     */
+    public boolean equals( final TarEntry other )
+    {
+        return getName().equals( other.getName() );
+    }
+
+    /**
+     * Parse an entry's header information from a header buffer.
+     *
+     * @param header The tar entry header buffer to get information from.
+     */
+    private void parseTarHeader( final byte[] header )
+    {
+        int offset = 0;
+
+        m_name = TarUtils.parseName( header, offset, NAMELEN );
+        offset += NAMELEN;
+        m_mode = (int)TarUtils.parseOctal( header, offset, TarEntry.MODELEN );
+        offset += TarEntry.MODELEN;
+        m_userID = (int)TarUtils.parseOctal( header, offset, TarEntry.UIDLEN );
+        offset += TarEntry.UIDLEN;
+        m_groupID = (int)TarUtils.parseOctal( header, offset, TarEntry.GIDLEN );
+        offset += TarEntry.GIDLEN;
+        m_size = TarUtils.parseOctal( header, offset, TarEntry.SIZELEN );
+        offset += TarEntry.SIZELEN;
+        m_modTime = TarUtils.parseOctal( header, offset, TarEntry.MODTIMELEN );
+        offset += TarEntry.MODTIMELEN;
+        m_checkSum = (int)TarUtils.parseOctal( header, offset, TarEntry.CHKSUMLEN );
+        offset += TarEntry.CHKSUMLEN;
+        m_linkFlag = header[ offset++ ];
+        m_linkName = TarUtils.parseName( header, offset, NAMELEN );
+        offset += NAMELEN;
+        m_magic = TarUtils.parseName( header, offset, TarEntry.MAGICLEN );
+        offset += TarEntry.MAGICLEN;
+        m_userName = TarUtils.parseName( header, offset, TarEntry.UNAMELEN );
+        offset += TarEntry.UNAMELEN;
+        m_groupName = TarUtils.parseName( header, offset, TarEntry.GNAMELEN );
+        offset += TarEntry.GNAMELEN;
+        m_devMajor = (int)TarUtils.parseOctal( header, offset, TarEntry.DEVLEN );
+        offset += TarEntry.DEVLEN;
+        m_devMinor = (int)TarUtils.parseOctal( header, offset, TarEntry.DEVLEN );
+    }
+
+    /**
+     * Write an entry's header information to a header buffer.
+     *
+     * @param buffer The tar entry header buffer to fill in.
+     */
+    public void writeEntryHeader( final byte[] buffer )
+    {
+        int offset = 0;
+
+        offset = TarUtils.getNameBytes( m_name, buffer, offset, NAMELEN );
+        offset = TarUtils.getOctalBytes( m_mode, buffer, offset, TarEntry.MODELEN );
+        offset = TarUtils.getOctalBytes( m_userID, buffer, offset, TarEntry.UIDLEN \
); +        offset = TarUtils.getOctalBytes( m_groupID, buffer, offset, \
TarEntry.GIDLEN ); +        offset = TarUtils.getLongOctalBytes( m_size, buffer, \
offset, TarEntry.SIZELEN ); +        offset = TarUtils.getLongOctalBytes( m_modTime, \
buffer, offset, TarEntry.MODTIMELEN ); +
+        final int checkSumOffset = offset;
+        for( int i = 0; i < TarEntry.CHKSUMLEN; ++i )
+        {
+            buffer[ offset++ ] = (byte)' ';
+        }
+
+        buffer[ offset++ ] = m_linkFlag;
+        offset = TarUtils.getNameBytes( m_linkName, buffer, offset, NAMELEN );
+        offset = TarUtils.getNameBytes( m_magic, buffer, offset, TarEntry.MAGICLEN \
); +        offset = TarUtils.getNameBytes( m_userName, buffer, offset, \
TarEntry.UNAMELEN ); +        offset = TarUtils.getNameBytes( m_groupName, buffer, \
offset, TarEntry.GNAMELEN ); +        offset = TarUtils.getOctalBytes( m_devMajor, \
buffer, offset, TarEntry.DEVLEN ); +        offset = TarUtils.getOctalBytes( \
m_devMinor, buffer, offset, TarEntry.DEVLEN ); +
+        while( offset < buffer.length )
+        {
+            buffer[ offset++ ] = 0;
+        }
+
+        final long checkSum = TarUtils.computeCheckSum( buffer );
+        TarUtils.getCheckSumOctalBytes( checkSum, buffer, checkSumOffset, \
TarEntry.CHKSUMLEN ); +    }
+}

Propchange: jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/tar/TarEntry.java
                
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/tar/TarEntry.java
                
------------------------------------------------------------------------------
    svn:executable = *

Propchange: jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/tar/TarEntry.java
                
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"

Added: jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/tar/TarInputStream.java
                
URL: http://svn.apache.org/viewvc/jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/tar/TarInputStream.java?rev=427072&view=auto
 ==============================================================================
--- jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/tar/TarInputStream.java \
                (added)
+++ jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/tar/TarInputStream.java \
Mon Jul 31 03:55:10 2006 @@ -0,0 +1,470 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.compress.archivers.tar;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+/**
+ * The TarInputStream reads a UNIX tar archive as an InputStream. methods are
+ * provided to position at each successive entry in the archive, and the read
+ * each entry as a normal input stream using read().
+ *
+ * @author <a href="mailto:time@ice.com">Timothy Gerard Endres</a>
+ * @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a>
+ * @author <a href="mailto:peter@apache.org">Peter Donald</a>
+ * @version $Revision: 155439 $ $Date$
+ * @see TarInputStream
+ * @see TarEntry
+ */
+public final class TarInputStream extends FilterInputStream
+{
+    private TarBuffer m_buffer;
+    private TarEntry m_currEntry;
+    private boolean m_debug;
+    private int m_entryOffset;
+    private int m_entrySize;
+    private boolean m_hasHitEOF;
+    private byte[] m_oneBuf;
+    private byte[] m_readBuf;
+
+    /**
+     * Construct a TarInputStream using specified input
+     * stream and default block and record sizes.
+     *
+     * @param input stream to create TarInputStream from
+     * @see TarBuffer#DEFAULT_BLOCKSIZE
+     * @see TarBuffer#DEFAULT_RECORDSIZE
+     */
+    public TarInputStream( final InputStream input )
+    {
+        this( input, TarBuffer.DEFAULT_BLOCKSIZE, TarBuffer.DEFAULT_RECORDSIZE );
+    }
+
+    /**
+     * Construct a TarInputStream using specified input
+     * stream, block size and default record sizes.
+     *
+     * @param input stream to create TarInputStream from
+     * @param blockSize the block size to use
+     * @see TarBuffer#DEFAULT_RECORDSIZE
+     */
+    public TarInputStream( final InputStream input,
+                           final int blockSize )
+    {
+        this( input, blockSize, TarBuffer.DEFAULT_RECORDSIZE );
+    }
+
+    /**
+     * Construct a TarInputStream using specified input
+     * stream, block size and record sizes.
+     *
+     * @param input stream to create TarInputStream from
+     * @param blockSize the block size to use
+     * @param recordSize the record size to use
+     */
+    public TarInputStream( final InputStream input,
+                           final int blockSize,
+                           final int recordSize )
+    {
+        super( input );
+
+        m_buffer = new TarBuffer( input, blockSize, recordSize );
+        m_oneBuf = new byte[ 1 ];
+    }
+
+    /**
+     * Sets the debugging flag.
+     *
+     * @param debug The new Debug value
+     */
+    public void setDebug( final boolean debug )
+    {
+        m_debug = debug;
+        m_buffer.setDebug( debug );
+    }
+
+    /**
+     * Get the next entry in this tar archive. This will skip over any remaining
+     * data in the current entry, if there is one, and place the input stream at
+     * the header of the next entry, and read the header and instantiate a new
+     * TarEntry from the header bytes and return that entry. If there are no
+     * more entries in the archive, null will be returned to indicate that the
+     * end of the archive has been reached.
+     *
+     * @return The next TarEntry in the archive, or null.
+     * @exception IOException Description of Exception
+     */
+    public TarEntry getNextEntry()
+        throws IOException
+    {
+        if( m_hasHitEOF )
+        {
+            return null;
+        }
+
+        if( m_currEntry != null )
+        {
+            final int numToSkip = m_entrySize - m_entryOffset;
+
+            if( m_debug )
+            {
+                final String message = "TarInputStream: SKIP currENTRY '" +
+                    m_currEntry.getName() + "' SZ " + m_entrySize +
+                    " OFF " + m_entryOffset + "  skipping " + numToSkip + " bytes";
+                debug( message );
+            }
+
+            if( numToSkip > 0 )
+            {
+                skip( numToSkip );
+            }
+
+            m_readBuf = null;
+        }
+
+        final byte[] headerBuf = m_buffer.readRecord();
+        if( headerBuf == null )
+        {
+            if( m_debug )
+            {
+                debug( "READ NULL RECORD" );
+            }
+            m_hasHitEOF = true;
+        }
+        else if( m_buffer.isEOFRecord( headerBuf ) )
+        {
+            if( m_debug )
+            {
+                debug( "READ EOF RECORD" );
+            }
+            m_hasHitEOF = true;
+        }
+
+        if( m_hasHitEOF )
+        {
+            m_currEntry = null;
+        }
+        else
+        {
+            m_currEntry = new TarEntry( headerBuf );
+
+            if( !( headerBuf[ 257 ] == 'u' && headerBuf[ 258 ] == 's' &&
+                headerBuf[ 259 ] == 't' && headerBuf[ 260 ] == 'a' &&
+                headerBuf[ 261 ] == 'r' ) )
+            {
+                //Must be v7Format
+            }
+
+            if( m_debug )
+            {
+                final String message = "TarInputStream: SET CURRENTRY '" +
+                    m_currEntry.getName() + "' size = " + m_currEntry.getSize();
+                debug( message );
+            }
+
+            m_entryOffset = 0;
+
+            // REVIEW How do we resolve this discrepancy?!
+            m_entrySize = (int)m_currEntry.getSize();
+        }
+
+        if( null != m_currEntry && m_currEntry.isGNULongNameEntry() )
+        {
+            // read in the name
+            final StringBuffer longName = new StringBuffer();
+            final byte[] buffer = new byte[ 256 ];
+            int length = 0;
+            while( ( length = read( buffer ) ) >= 0 )
+            {
+                final String str = new String( buffer, 0, length );
+                longName.append( str );
+            }
+            getNextEntry();
+
+            // remove trailing null terminator
+            if (longName.length() > 0
+                && longName.charAt(longName.length() - 1) == 0) {
+                longName.deleteCharAt(longName.length() - 1);
+            }
+            
+            m_currEntry.setName( longName.toString() );
+        }
+
+        return m_currEntry;
+    }
+
+    /**
+     * Get the record size being used by this stream's TarBuffer.
+     *
+     * @return The TarBuffer record size.
+     */
+    public int getRecordSize()
+    {
+        return m_buffer.getRecordSize();
+    }
+
+    /**
+     * Get the available data that can be read from the current entry in the
+     * archive. This does not indicate how much data is left in the entire
+     * archive, only in the current entry. This value is determined from the
+     * entry's size header field and the amount of data already read from the
+     * current entry.
+     *
+     * @return The number of available bytes for the current entry.
+     * @exception IOException when an IO error causes operation to fail
+     */
+    public int available()
+        throws IOException
+    {
+        return m_entrySize - m_entryOffset;
+    }
+
+    /**
+     * Closes this stream. Calls the TarBuffer's close() method.
+     *
+     * @exception IOException when an IO error causes operation to fail
+     */
+    public void close()
+        throws IOException
+    {
+        m_buffer.close();
+    }
+
+    /**
+     * Copies the contents of the current tar archive entry directly into an
+     * output stream.
+     *
+     * @param output The OutputStream into which to write the entry's data.
+     * @exception IOException when an IO error causes operation to fail
+     */
+    public void copyEntryContents( final OutputStream output )
+        throws IOException
+    {
+        final byte[] buffer = new byte[ 32 * 1024 ];
+        while( true )
+        {
+            final int numRead = read( buffer, 0, buffer.length );
+            if( numRead == -1 )
+            {
+                break;
+            }
+
+            output.write( buffer, 0, numRead );
+        }
+    }
+
+    /**
+     * Since we do not support marking just yet, we do nothing.
+     *
+     * @param markLimit The limit to mark.
+     */
+    public void mark( int markLimit )
+    {
+    }
+
+    /**
+     * Since we do not support marking just yet, we return false.
+     *
+     * @return False.
+     */
+    public boolean markSupported()
+    {
+        return false;
+    }
+
+    /**
+     * Reads a byte from the current tar archive entry. This method simply calls
+     * read( byte[], int, int ).
+     *
+     * @return The byte read, or -1 at EOF.
+     * @exception IOException when an IO error causes operation to fail
+     */
+    public int read()
+        throws IOException
+    {
+        final int num = read( m_oneBuf, 0, 1 );
+        if( num == -1 )
+        {
+            return num;
+        }
+        else
+        {
+            return (int)m_oneBuf[ 0 ];
+        }
+    }
+
+    /**
+     * Reads bytes from the current tar archive entry. This method simply calls
+     * read( byte[], int, int ).
+     *
+     * @param buffer The buffer into which to place bytes read.
+     * @return The number of bytes read, or -1 at EOF.
+     * @exception IOException when an IO error causes operation to fail
+     */
+    public int read( final byte[] buffer )
+        throws IOException
+    {
+        return read( buffer, 0, buffer.length );
+    }
+
+    /**
+     * Reads bytes from the current tar archive entry. This method is aware of
+     * the boundaries of the current entry in the archive and will deal with
+     * them as if they were this stream's start and EOF.
+     *
+     * @param buffer The buffer into which to place bytes read.
+     * @param offset The offset at which to place bytes read.
+     * @param count The number of bytes to read.
+     * @return The number of bytes read, or -1 at EOF.
+     * @exception IOException when an IO error causes operation to fail
+     */
+    public int read( final byte[] buffer,
+                     final int offset,
+                     final int count )
+        throws IOException
+    {
+        int position = offset;
+        int numToRead = count;
+        int totalRead = 0;
+
+        if( m_entryOffset >= m_entrySize )
+        {
+            return -1;
+        }
+
+        if( ( numToRead + m_entryOffset ) > m_entrySize )
+        {
+            numToRead = ( m_entrySize - m_entryOffset );
+        }
+
+        if( null != m_readBuf )
+        {
+            final int size =
+                ( numToRead > m_readBuf.length ) ? m_readBuf.length : numToRead;
+
+            System.arraycopy( m_readBuf, 0, buffer, position, size );
+
+            if( size >= m_readBuf.length )
+            {
+                m_readBuf = null;
+            }
+            else
+            {
+                final int newLength = m_readBuf.length - size;
+                final byte[] newBuffer = new byte[ newLength ];
+
+                System.arraycopy( m_readBuf, size, newBuffer, 0, newLength );
+
+                m_readBuf = newBuffer;
+            }
+
+            totalRead += size;
+            numToRead -= size;
+            position += size;
+        }
+
+        while( numToRead > 0 )
+        {
+            final byte[] rec = m_buffer.readRecord();
+            if( null == rec )
+            {
+                // Unexpected EOF!
+                final String message =
+                    "unexpected EOF with " + numToRead + " bytes unread";
+                throw new IOException( message );
+            }
+
+            int size = numToRead;
+            final int recordLength = rec.length;
+
+            if( recordLength > size )
+            {
+                System.arraycopy( rec, 0, buffer, position, size );
+
+                m_readBuf = new byte[ recordLength - size ];
+
+                System.arraycopy( rec, size, m_readBuf, 0, recordLength - size );
+            }
+            else
+            {
+                size = recordLength;
+
+                System.arraycopy( rec, 0, buffer, position, recordLength );
+            }
+
+            totalRead += size;
+            numToRead -= size;
+            position += size;
+        }
+
+        m_entryOffset += totalRead;
+
+        return totalRead;
+    }
+
+    /**
+     * Since we do not support marking just yet, we do nothing.
+     */
+    public void reset()
+    {
+    }
+
+    /**
+     * Skip bytes in the input buffer. This skips bytes in the current entry's
+     * data, not the entire archive, and will stop at the end of the current
+     * entry's data if the number to skip extends beyond that point.
+     *
+     * @param numToSkip The number of bytes to skip.
+     * @exception IOException when an IO error causes operation to fail
+     */
+    public void skip( final int numToSkip )
+        throws IOException
+    {
+        // REVIEW
+        // This is horribly inefficient, but it ensures that we
+        // properly skip over bytes via the TarBuffer...
+        //
+        final byte[] skipBuf = new byte[ 8 * 1024 ];
+        int num = numToSkip;
+        while( num > 0 )
+        {
+            final int count = ( num > skipBuf.length ) ? skipBuf.length : num;
+            final int numRead = read( skipBuf, 0, count );
+            if( numRead == -1 )
+            {
+                break;
+            }
+
+            num -= numRead;
+        }
+    }
+
+    /**
+     * Utility method to do debugging.
+     * Capable of being overidden in sub-classes.
+     *
+     * @param message the message to use in debugging
+     */
+    protected void debug( final String message )
+    {
+        if( m_debug )
+        {
+            System.err.println( message );
+        }
+    }
+}

Propchange: jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/tar/TarInputStream.java
                
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/tar/TarInputStream.java
                
------------------------------------------------------------------------------
    svn:executable = *

Propchange: jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/tar/TarInputStream.java
                
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"

Added: jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/tar/TarOutputStream.java
                
URL: http://svn.apache.org/viewvc/jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/tar/TarOutputStream.java?rev=427072&view=auto
 ==============================================================================
--- jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/tar/TarOutputStream.java \
                (added)
+++ jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/tar/TarOutputStream.java \
Mon Jul 31 03:55:10 2006 @@ -0,0 +1,422 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.compress.archivers.tar;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+/**
+ * The TarOutputStream writes a UNIX tar archive as an OutputStream. Methods are
+ * provided to put entries, and then write their contents by writing to this
+ * stream using write().
+ *
+ * @author Timothy Gerard Endres <a href="mailto:time@ice.com">time@ice.com</a>
+ * @author <a href="mailto:peter@apache.org">Peter Donald</a>
+ * @version $Revision: 155439 $ $Date$
+ * @see TarInputStream
+ * @see TarEntry
+ */
+public final class TarOutputStream extends FilterOutputStream
+{
+    /**
+     * Flag to indicate that an error should be generated if
+     * an attempt is made to write an entry that exceeds the 100 char
+     * POSIX limit.
+     */
+    public static final int LONGFILE_ERROR = 0;
+
+    /**
+     * Flag to indicate that entry name should be truncated if
+     * an attempt is made to write an entry that exceeds the 100 char
+     * POSIX limit.
+     */
+    public static final int LONGFILE_TRUNCATE = 1;
+
+    /**
+     * Flag to indicate that entry name should be formatted
+     * according to GNU tar extension if an attempt is made
+     * to write an entry that exceeds the 100 char POSIX
+     * limit. Note that this makes the jar unreadable by
+     * non-GNU tar commands.
+     */
+    public static final int LONGFILE_GNU = 2;
+
+    private int m_longFileMode = LONGFILE_ERROR;
+    private byte[] m_assemBuf;
+    private int m_assemLen;
+    private TarBuffer m_buffer;
+    private int m_currBytes;
+    private int m_currSize;
+
+    private byte[] m_oneBuf;
+    private byte[] m_recordBuf;
+
+    /**
+     * Construct a TarOutputStream using specified input
+     * stream and default block and record sizes.
+     *
+     * @param output stream to create TarOutputStream from
+     * @see TarBuffer#DEFAULT_BLOCKSIZE
+     * @see TarBuffer#DEFAULT_RECORDSIZE
+     */
+    public TarOutputStream( final OutputStream output )
+    {
+        this( output, TarBuffer.DEFAULT_BLOCKSIZE, TarBuffer.DEFAULT_RECORDSIZE );
+    }
+
+    /**
+     * Construct a TarOutputStream using specified input
+     * stream, block size and default record sizes.
+     *
+     * @param output stream to create TarOutputStream from
+     * @param blockSize the block size
+     * @see TarBuffer#DEFAULT_RECORDSIZE
+     */
+    public TarOutputStream( final OutputStream output,
+                            final int blockSize )
+    {
+        this( output, blockSize, TarBuffer.DEFAULT_RECORDSIZE );
+    }
+
+    /**
+     * Construct a TarOutputStream using specified input
+     * stream, block size and record sizes.
+     *
+     * @param output stream to create TarOutputStream from
+     * @param blockSize the block size
+     * @param recordSize the record size
+     */
+    public TarOutputStream( final OutputStream output,
+                            final int blockSize,
+                            final int recordSize )
+    {
+        super( output );
+
+        m_buffer = new TarBuffer( output, blockSize, recordSize );
+        m_assemLen = 0;
+        m_assemBuf = new byte[ recordSize ];
+        m_recordBuf = new byte[ recordSize ];
+        m_oneBuf = new byte[ 1 ];
+    }
+
+    /**
+     * Sets the debugging flag in this stream's TarBuffer.
+     *
+     * @param debug The new BufferDebug value
+     */
+    public void setBufferDebug( boolean debug )
+    {
+        m_buffer.setDebug( debug );
+    }
+
+    /**
+     * Set the mode used to work with entrys exceeding
+     * 100 chars (and thus break the POSIX standard).
+     * Must be one of the LONGFILE_* constants.
+     *
+     * @param longFileMode the mode
+     */
+    public void setLongFileMode( final int longFileMode )
+    {
+        if( LONGFILE_ERROR != longFileMode &&
+            LONGFILE_GNU != longFileMode &&
+            LONGFILE_TRUNCATE != longFileMode )
+        {
+            throw new IllegalArgumentException( "longFileMode" );
+        }
+        m_longFileMode = longFileMode;
+    }
+
+    /**
+     * Get the record size being used by this stream's TarBuffer.
+     *
+     * @return The TarBuffer record size.
+     */
+    public int getRecordSize()
+    {
+        return m_buffer.getRecordSize();
+    }
+
+    /**
+     * Ends the TAR archive and closes the underlying OutputStream. This means
+     * that finish() is called followed by calling the TarBuffer's close().
+     *
+     * @exception IOException when an IO error causes operation to fail
+     */
+    public void close()
+        throws IOException
+    {
+        finish();
+        m_buffer.close();
+    }
+
+    /**
+     * Close an entry. This method MUST be called for all file entries that
+     * contain data. The reason is that we must buffer data written to the
+     * stream in order to satisfy the buffer's record based writes. Thus, there
+     * may be data fragments still being assembled that must be written to the
+     * output stream before this entry is closed and the next entry written.
+     *
+     * @exception IOException when an IO error causes operation to fail
+     */
+    public void closeEntry()
+        throws IOException
+    {
+        if( m_assemLen > 0 )
+        {
+            for( int i = m_assemLen; i < m_assemBuf.length; ++i )
+            {
+                m_assemBuf[ i ] = 0;
+            }
+
+            m_buffer.writeRecord( m_assemBuf );
+
+            m_currBytes += m_assemLen;
+            m_assemLen = 0;
+        }
+
+        if( m_currBytes < m_currSize )
+        {
+            final String message = "entry closed at '" + m_currBytes +
+                "' before the '" + m_currSize +
+                "' bytes specified in the header were written";
+            throw new IOException( message );
+        }
+    }
+
+    /**
+     * Ends the TAR archive without closing the underlying OutputStream. The
+     * result is that the EOF record of nulls is written.
+     *
+     * @exception IOException when an IO error causes operation to fail
+     */
+    public void finish()
+        throws IOException
+    {
+        writeEOFRecord();
+    }
+
+    /**
+     * Put an entry on the output stream. This writes the entry's header record
+     * and positions the output stream for writing the contents of the entry.
+     * Once this method is called, the stream is ready for calls to write() to
+     * write the entry's contents. Once the contents are written, closeEntry()
+     * <B>MUST</B> be called to ensure that all buffered data is completely
+     * written to the output stream.
+     *
+     * @param entry The TarEntry to be written to the archive.
+     * @exception IOException when an IO error causes operation to fail
+     */
+    public void putNextEntry( final TarEntry entry )
+        throws IOException
+    {
+        if( entry.getName().length() >= TarEntry.NAMELEN )
+        {
+            if( m_longFileMode == LONGFILE_GNU )
+            {
+                // create a TarEntry for the LongLink, the contents
+                // of which are the entry's name
+                final TarEntry longLinkEntry =
+                    new TarEntry( TarEntry.GNU_LONGLINK,
+                                  TarEntry.LF_GNUTYPE_LONGNAME );
+
+                longLinkEntry.setSize( entry.getName().length() );
+                putNextEntry( longLinkEntry );
+                write( entry.getName().getBytes() );
+                //write( 0 );
+                closeEntry();
+            }
+            else if( m_longFileMode != LONGFILE_TRUNCATE )
+            {
+                final String message = "file name '" + entry.getName() +
+                    "' is too long ( > " + TarEntry.NAMELEN + " bytes)";
+                throw new IOException( message );
+            }
+        }
+
+        entry.writeEntryHeader( m_recordBuf );
+        m_buffer.writeRecord( m_recordBuf );
+
+        m_currBytes = 0;
+
+        if( entry.isDirectory() )
+        {
+            m_currSize = 0;
+        }
+        else
+        {
+            m_currSize = (int)entry.getSize();
+        }
+    }
+
+    /**
+     * Copies the contents of the specified stream into current tar
+     * archive entry.
+     *
+     * @param input The InputStream from which to read entrys data
+     * @exception IOException when an IO error causes operation to fail
+     */
+    void copyEntryContents( final InputStream input )
+        throws IOException
+    {
+        final byte[] buffer = new byte[ 32 * 1024 ];
+        while( true )
+        {
+            final int numRead = input.read( buffer, 0, buffer.length );
+            if( numRead == -1 )
+            {
+                break;
+            }
+
+            write( buffer, 0, numRead );
+        }
+    }
+
+    /**
+     * Writes a byte to the current tar archive entry. This method simply calls
+     * read( byte[], int, int ).
+     *
+     * @param data The byte written.
+     * @exception IOException when an IO error causes operation to fail
+     */
+    public void write( final int data )
+        throws IOException
+    {
+        m_oneBuf[ 0 ] = (byte)data;
+
+        write( m_oneBuf, 0, 1 );
+    }
+
+    /**
+     * Writes bytes to the current tar archive entry. This method simply calls
+     * write( byte[], int, int ).
+     *
+     * @param buffer The buffer to write to the archive.
+     * @exception IOException when an IO error causes operation to fail
+     */
+    public void write( final byte[] buffer )
+        throws IOException
+    {
+        write( buffer, 0, buffer.length );
+    }
+
+    /**
+     * Writes bytes to the current tar archive entry. This method is aware of
+     * the current entry and will throw an exception if you attempt to write
+     * bytes past the length specified for the current entry. The method is also
+     * (painfully) aware of the record buffering required by TarBuffer, and
+     * manages buffers that are not a multiple of recordsize in length,
+     * including assembling records from small buffers.
+     *
+     * @param buffer The buffer to write to the archive.
+     * @param offset The offset in the buffer from which to get bytes.
+     * @param count The number of bytes to write.
+     * @exception IOException when an IO error causes operation to fail
+     */
+    public void write( final byte[] buffer,
+                       final int offset,
+                       final int count )
+        throws IOException
+    {
+        int position = offset;
+        int numToWrite = count;
+        if( ( m_currBytes + numToWrite ) > m_currSize )
+        {
+            final String message = "request to write '" + numToWrite +
+                "' bytes exceeds size in header of '" + m_currSize + "' bytes";
+            throw new IOException( message );
+            //
+            // We have to deal with assembly!!!
+            // The programmer can be writing little 32 byte chunks for all
+            // we know, and we must assemble complete records for writing.
+            // REVIEW Maybe this should be in TarBuffer? Could that help to
+            // eliminate some of the buffer copying.
+            //
+        }
+
+        if( m_assemLen > 0 )
+        {
+            if( ( m_assemLen + numToWrite ) >= m_recordBuf.length )
+            {
+                final int length = m_recordBuf.length - m_assemLen;
+
+                System.arraycopy( m_assemBuf, 0, m_recordBuf, 0,
+                                  m_assemLen );
+                System.arraycopy( buffer, position, m_recordBuf,
+                                  m_assemLen, length );
+                m_buffer.writeRecord( m_recordBuf );
+
+                m_currBytes += m_recordBuf.length;
+                position += length;
+                numToWrite -= length;
+                m_assemLen = 0;
+            }
+            else
+            {
+                System.arraycopy( buffer, position, m_assemBuf, m_assemLen,
+                                  numToWrite );
+
+                position += numToWrite;
+                m_assemLen += numToWrite;
+                numToWrite -= numToWrite;
+            }
+        }
+
+        //
+        // When we get here we have EITHER:
+        // o An empty "assemble" buffer.
+        // o No bytes to write (numToWrite == 0)
+        //
+        while( numToWrite > 0 )
+        {
+            if( numToWrite < m_recordBuf.length )
+            {
+                System.arraycopy( buffer, position, m_assemBuf, m_assemLen,
+                                  numToWrite );
+
+                m_assemLen += numToWrite;
+
+                break;
+            }
+
+            m_buffer.writeRecord( buffer, position );
+
+            int num = m_recordBuf.length;
+
+            m_currBytes += num;
+            numToWrite -= num;
+            position += num;
+        }
+    }
+
+    /**
+     * Write an EOF (end of archive) record to the tar archive. An EOF record
+     * consists of a record of all zeros.
+     *
+     * @exception IOException when an IO error causes operation to fail
+     */
+    private void writeEOFRecord()
+        throws IOException
+    {
+        for( int i = 0; i < m_recordBuf.length; ++i )
+        {
+            m_recordBuf[ i ] = 0;
+        }
+
+        m_buffer.writeRecord( m_recordBuf );
+    }
+}

Propchange: jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/tar/TarOutputStream.java
                
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/tar/TarOutputStream.java
                
------------------------------------------------------------------------------
    svn:executable = *

Propchange: jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/tar/TarOutputStream.java
                
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"

Added: jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/tar/TarUtils.java
                
URL: http://svn.apache.org/viewvc/jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/tar/TarUtils.java?rev=427072&view=auto
 ==============================================================================
--- jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/tar/TarUtils.java \
                (added)
+++ jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/tar/TarUtils.java \
Mon Jul 31 03:55:10 2006 @@ -0,0 +1,233 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.compress.archivers.tar;
+
+/**
+ * This class provides static utility methods to work with byte streams.
+ *
+ * @author <a href="mailto:time@ice.com">Timothy Gerard Endres</a>
+ * @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a>
+ * @version $Revision: 155439 $ $Date$
+ */
+public class TarUtils
+{
+    /**
+     * Parse the checksum octal integer from a header buffer.
+     *
+     * @param offset The offset into the buffer from which to parse.
+     * @param length The number of header bytes to parse.
+     * @param value Description of Parameter
+     * @param buf Description of Parameter
+     * @return The integer value of the entry's checksum.
+     */
+    public static int getCheckSumOctalBytes( final long value,
+                                             final byte[] buf,
+                                             final int offset,
+                                             final int length )
+    {
+        getOctalBytes( value, buf, offset, length );
+
+        buf[ offset + length - 1 ] = (byte)' ';
+        buf[ offset + length - 2 ] = 0;
+
+        return offset + length;
+    }
+
+    /**
+     * Parse an octal long integer from a header buffer.
+     *
+     * @param offset The offset into the buffer from which to parse.
+     * @param length The number of header bytes to parse.
+     * @param value Description of Parameter
+     * @param buf Description of Parameter
+     * @return The long value of the octal bytes.
+     */
+    public static int getLongOctalBytes( final long value,
+                                         final byte[] buf,
+                                         final int offset,
+                                         final int length )
+    {
+        byte[] temp = new byte[ length + 1 ];
+
+        getOctalBytes( value, temp, 0, length + 1 );
+        System.arraycopy( temp, 0, buf, offset, length );
+
+        return offset + length;
+    }
+
+    /**
+     * Determine the number of bytes in an entry name.
+     *
+     * @param offset The offset into the buffer from which to parse.
+     * @param length The number of header bytes to parse.
+     * @param name Description of Parameter
+     * @param buffer Description of Parameter
+     * @return The number of bytes in a header's entry name.
+     */
+    public static int getNameBytes( final StringBuffer name,
+                                    final byte[] buffer,
+                                    final int offset,
+                                    final int length )
+    {
+        int i;
+
+        for( i = 0; i < length && i < name.length(); ++i )
+        {
+            buffer[ offset + i ] = (byte)name.charAt( i );
+        }
+
+        for( ; i < length; ++i )
+        {
+            buffer[ offset + i ] = 0;
+        }
+
+        return offset + length;
+    }
+
+    /**
+     * Parse an octal integer from a header buffer.
+     *
+     * @param offset The offset into the buffer from which to parse.
+     * @param length The number of header bytes to parse.
+     * @return The integer value of the octal bytes.
+     */
+    public static int getOctalBytes( final long value,
+                                     final byte[] buffer,
+                                     final int offset,
+                                     final int length )
+    {
+        int idx = length - 1;
+
+        buffer[ offset + idx ] = 0;
+        --idx;
+        buffer[ offset + idx ] = (byte)' ';
+        --idx;
+
+        if( value == 0 )
+        {
+            buffer[ offset + idx ] = (byte)'0';
+            --idx;
+        }
+        else
+        {
+            long val = value;
+            while( idx >= 0 && val > 0 )
+            {
+                buffer[ offset + idx ] = (byte)( (byte)'0' + (byte)( val & 7 ) );
+                val = val >> 3;
+                idx--;
+            }
+        }
+
+        while( idx >= 0 )
+        {
+            buffer[ offset + idx ] = (byte)' ';
+            idx--;
+        }
+
+        return offset + length;
+    }
+
+    /**
+     * Compute the checksum of a tar entry header.
+     *
+     * @param buffer The tar entry's header buffer.
+     * @return The computed checksum.
+     */
+    public static long computeCheckSum( final byte[] buffer )
+    {
+        long sum = 0;
+
+        for( int i = 0; i < buffer.length; ++i )
+        {
+            sum += 255 & buffer[ i ];
+        }
+
+        return sum;
+    }
+
+    /**
+     * Parse an entry name from a header buffer.
+     *
+     * @param header The header buffer from which to parse.
+     * @param offset The offset into the buffer from which to parse.
+     * @param length The number of header bytes to parse.
+     * @return The header's entry name.
+     */
+    public static StringBuffer parseName( final byte[] header,
+                                          final int offset,
+                                          final int length )
+    {
+        StringBuffer result = new StringBuffer( length );
+        int end = offset + length;
+
+        for( int i = offset; i < end; ++i )
+        {
+            if( header[ i ] == 0 )
+            {
+                break;
+            }
+
+            result.append( (char)header[ i ] );
+        }
+
+        return result;
+    }
+
+    /**
+     * Parse an octal string from a header buffer. This is used for the file
+     * permission mode value.
+     *
+     * @param header The header buffer from which to parse.
+     * @param offset The offset into the buffer from which to parse.
+     * @param length The number of header bytes to parse.
+     * @return The long value of the octal string.
+     */
+    public static long parseOctal( final byte[] header,
+                                   final int offset,
+                                   final int length )
+    {
+        long result = 0;
+        boolean stillPadding = true;
+        int end = offset + length;
+
+        for( int i = offset; i < end; ++i )
+        {
+            if( header[ i ] == 0 )
+            {
+                break;
+            }
+
+            if( header[ i ] == (byte)' ' || header[ i ] == '0' )
+            {
+                if( stillPadding )
+                {
+                    continue;
+                }
+
+                if( header[ i ] == (byte)' ' )
+                {
+                    break;
+                }
+            }
+
+            stillPadding = false;
+            result = ( result << 3 ) + ( header[ i ] - '0' );
+        }
+
+        return result;
+    }
+}

Propchange: jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/tar/TarUtils.java
                
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/tar/TarUtils.java
                
------------------------------------------------------------------------------
    svn:executable = *

Propchange: jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/tar/TarUtils.java
                
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"

Added: jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/zip/AsiExtraField.java
                
URL: http://svn.apache.org/viewvc/jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/zip/AsiExtraField.java?rev=427072&view=auto
 ==============================================================================
--- jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/zip/AsiExtraField.java \
                (added)
+++ jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/zip/AsiExtraField.java \
Mon Jul 31 03:55:10 2006 @@ -0,0 +1,406 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.compress.archivers.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. <p>
+ *
+ * This class uses the ASi extra field in the format: <pre>
+ *         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
+ * </pre> taken from appnote.iz (Info-ZIP note, 981119) found at <a
+ * href="ftp://ftp.uu.net/pub/archiving/zip/doc/">
+ * ftp://ftp.uu.net/pub/archiving/zip/doc/</a> </p> <p>
+ *
+ * Short is two bytes and Long is four bytes in big endian byte and word order,
+ * device numbers are currently not supported.</p>
+ *
+ * @author <a href="stefan.bodewig@epost.de">Stefan Bodewig</a>
+ * @version $Revision: 155439 $
+ */
+public class AsiExtraField
+    implements ZipExtraField, UnixStat, Cloneable
+{
+    private static final ZipShort HEADER_ID = new ZipShort( 0x756E );
+
+    /**
+     * Standard Unix stat(2) file mode.
+     *
+     * @since 1.1
+     */
+    private int m_mode;
+
+    /**
+     * User ID.
+     *
+     * @since 1.1
+     */
+    private int m_uid;
+
+    /**
+     * Group ID.
+     *
+     * @since 1.1
+     */
+    private int m_gid;
+
+    /**
+     * File this entry points to, if it is a symbolic link. <p>
+     *
+     * empty string - if entry is not a symbolic link.</p>
+     *
+     * @since 1.1
+     */
+    private String m_link = "";
+
+    /**
+     * Is this an entry for a directory?
+     *
+     * @since 1.1
+     */
+    private boolean m_dirFlag;
+
+    /**
+     * Instance used to calculate checksums.
+     *
+     * @since 1.1
+     */
+    private CRC32 m_crc = new CRC32();
+
+    /**
+     * Indicate whether this entry is a directory.
+     *
+     * @param dirFlag The new Directory value
+     * @since 1.1
+     */
+    public void setDirectory( final boolean dirFlag )
+    {
+        m_dirFlag = dirFlag;
+        m_mode = getMode( m_mode );
+    }
+
+    /**
+     * Set the group id.
+     *
+     * @param gid The new GroupId value
+     * @since 1.1
+     */
+    public void setGroupId( int gid )
+    {
+        m_gid = 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( final String name )
+    {
+        m_link = name;
+        m_mode = getMode( m_mode );
+    }
+
+    /**
+     * File mode of this file.
+     *
+     * @param mode The new Mode value
+     * @since 1.1
+     */
+    public void setMode( final int mode )
+    {
+        m_mode = getMode( mode );
+    }
+
+    /**
+     * Set the user id.
+     *
+     * @param uid The new UserId value
+     * @since 1.1
+     * @deprecated Use setUserID(int)
+     * @see #setUserID(int)
+     */
+    public void setUserId( final int uid )
+    {
+        m_uid = uid;
+    }
+
+    /**
+     * Set the user id.
+     *
+     * @param uid The new UserId value
+     */
+    public void setUserID( final int uid )
+    {
+        m_uid = uid;
+    }
+
+    /**
+     * Delegate to local file data.
+     *
+     * @return The CentralDirectoryData value
+     * @since 1.1
+     */
+    public byte[] getCentralDirectoryData()
+    {
+        return getLocalFileDataData();
+    }
+
+    /**
+     * Delegate to local file data.
+     *
+     * @return The CentralDirectoryLength value
+     * @since 1.1
+     */
+    public ZipShort getCentralDirectoryLength()
+    {
+        return getLocalFileDataLength();
+    }
+
+    /**
+     * Get the group id.
+     *
+     * @return The GroupId value
+     * @since 1.1
+     */
+    public int getGroupID()
+    {
+        return m_gid;
+    }
+
+    /**
+     * Get the group id.
+     *
+     * @return The GroupId value
+     * @since 1.1
+     * @deprecated Use getGroupID() instead
+     * @see #getGroupID()
+     */
+    public int getGroupId()
+    {
+        return m_gid;
+    }
+
+    /**
+     * The Header-ID.
+     *
+     * @return The HeaderId value
+     * @since 1.1
+     */
+    public ZipShort getHeaderID()
+    {
+        return HEADER_ID;
+    }
+
+    /**
+     * 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 m_link;
+    }
+
+    /**
+     * The actual data to put into local file data - without Header-ID or length
+     * specifier.
+     *
+     * @return The LocalFileDataData value
+     * @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 );
+
+        m_crc.reset();
+        m_crc.update( data );
+        long checksum = m_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;
+    }
+
+    /**
+     * Length of the extra field in the local file data - without Header-ID or
+     * length specifier.
+     *
+     * @return The LocalFileDataLength value
+     * @since 1.1
+     */
+    public ZipShort getLocalFileDataLength()
+    {
+        return new ZipShort( 4 + // CRC
+                             2 + // Mode
+                             4 + // SizDev
+                             2 + // UID
+                             2 + // GID
+                             getLinkedFile().getBytes().length );
+    }
+
+    /**
+     * File mode of this file.
+     *
+     * @return The Mode value
+     * @since 1.1
+     */
+    public int getMode()
+    {
+        return m_mode;
+    }
+
+    /**
+     * Get the user id.
+     *
+     * @return The UserId value
+     * @since 1.1
+     * @deprecated Use getUserID()
+     * @see #getUserID()
+     */
+    public int getUserId()
+    {
+        return m_uid;
+    }
+
+    /**
+     * Get the user id.
+     *
+     * @return The UserID value
+     */
+    public int getUserID()
+    {
+        return m_uid;
+    }
+
+    /**
+     * Is this entry a directory?
+     *
+     * @return The Directory value
+     * @since 1.1
+     */
+    public boolean isDirectory()
+    {
+        return m_dirFlag && !isLink();
+    }
+
+    /**
+     * Is this entry a symbolic link?
+     *
+     * @return The Link value
+     * @since 1.1
+     */
+    public boolean isLink()
+    {
+        return getLinkedFile().length() != 0;
+    }
+
+    /**
+     * Populate data from this array as if it was in local file data.
+     *
+     * @param buffer the buffer
+     * @param offset the offset into buffer
+     * @param length the length of data in buffer
+     * @throws ZipException on error
+     * @since 1.1
+     */
+    public void parseFromLocalFileData( final byte[] buffer,
+                                        final int offset,
+                                        final int length )
+        throws ZipException
+    {
+
+        long givenChecksum = ( new ZipLong( buffer, offset ) ).getValue();
+        byte[] tmp = new byte[ length - 4 ];
+        System.arraycopy( buffer, offset + 4, tmp, 0, length - 4 );
+        m_crc.reset();
+        m_crc.update( tmp );
+        long realChecksum = m_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() ];
+        m_uid = ( new ZipShort( tmp, 6 ) ).getValue();
+        m_gid = ( new ZipShort( tmp, 8 ) ).getValue();
+
+        if( linkArray.length == 0 )
+        {
+            m_link = "";
+        }
+        else
+        {
+            System.arraycopy( tmp, 10, linkArray, 0, linkArray.length );
+            m_link = new String( linkArray );
+        }
+        setDirectory( ( newMode & DIR_FLAG ) != 0 );
+        setMode( newMode );
+    }
+
+    /**
+     * Get the file mode for given permissions with the correct file type.
+     *
+     * @param mode Description of Parameter
+     * @return The Mode value
+     * @since 1.1
+     */
+    protected int getMode( final int mode )
+    {
+        int type = FILE_FLAG;
+        if( isLink() )
+        {
+            type = LINK_FLAG;
+        }
+        else if( isDirectory() )
+        {
+            type = DIR_FLAG;
+        }
+        return type | ( mode & PERM_MASK );
+    }
+}

Propchange: jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/zip/AsiExtraField.java
                
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/zip/AsiExtraField.java
                
------------------------------------------------------------------------------
    svn:executable = *

Propchange: jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/zip/AsiExtraField.java
                
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"

Added: jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtils.java
                
URL: http://svn.apache.org/viewvc/jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtils.java?rev=427072&view=auto
 ==============================================================================
--- jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtils.java \
                (added)
+++ jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtils.java \
Mon Jul 31 03:55:10 2006 @@ -0,0 +1,204 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.compress.archivers.zip;
+
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.zip.ZipException;
+
+/**
+ * ZipExtraField related methods
+ *
+ * @author <a href="stefan.bodewig@epost.de">Stefan Bodewig</a>
+ * @version $Revision: 155439 $
+ */
+public class ExtraFieldUtils
+{
+    /**
+     * Static registry of known extra fields.
+     *
+     * @since 1.1
+     */
+    private static final Hashtable c_implementations;
+
+    static
+    {
+        c_implementations = new Hashtable();
+        register( AsiExtraField.class );
+    }
+
+    /**
+     * Create an instance of the approriate ExtraField, falls back to {@link
+     * UnrecognizedExtraField UnrecognizedExtraField}.
+     *
+     * Throws java.lang.IllegalAccessException if cant create implementation.
+     *
+     * @param headerID the header ID
+     * @return the extra field implementation
+     * @throws InstantiationException if cant create implementation
+     * @throws IllegalAccessException if cant create implementation
+     * @since 1.1
+     */
+    public static ZipExtraField createExtraField( final ZipShort headerID )
+        throws InstantiationException, IllegalAccessException
+    {
+        final Class clazz =
+            (Class)c_implementations.get( headerID );
+        if( clazz != null )
+        {
+            return (ZipExtraField)clazz.newInstance();
+        }
+        final UnrecognizedExtraField unrecognized = new UnrecognizedExtraField();
+        unrecognized.setHeaderID( headerID );
+        return unrecognized;
+    }
+
+    /**
+     * Merges the central directory fields of the given ZipExtraFields.
+     *
+     * @param data the central directory data
+     * @return the merged data
+     * @since 1.1
+     */
+    public static byte[] mergeCentralDirectoryData( final ZipExtraField[] data )
+    {
+        int sum = 4 * data.length;
+        for( int i = 0; i < data.length; i++ )
+        {
+            sum += data[ i ].getCentralDirectoryLength().getValue();
+        }
+        byte[] result = new byte[ sum ];
+        int start = 0;
+        for( int i = 0; i < data.length; i++ )
+        {
+            System.arraycopy( data[ i ].getHeaderID().getBytes(),
+                              0, result, start, 2 );
+            System.arraycopy( data[ i ].getCentralDirectoryLength().getBytes(),
+                              0, result, start + 2, 2 );
+            byte[] local = data[ i ].getCentralDirectoryData();
+            System.arraycopy( local, 0, result, start + 4, local.length );
+            start += ( local.length + 4 );
+        }
+        return result;
+    }
+
+    /**
+     * Merges the local file data fields of the given ZipExtraFields.
+     *
+     * @param data the data
+     * @return the merged data
+     * @since 1.1
+     */
+    public static byte[] mergeLocalFileDataData( final ZipExtraField[] data )
+    {
+        int sum = 4 * data.length;
+        for( int i = 0; i < data.length; i++ )
+        {
+            sum += data[ i ].getLocalFileDataLength().getValue();
+        }
+        byte[] result = new byte[ sum ];
+        int start = 0;
+        for( int i = 0; i < data.length; i++ )
+        {
+            System.arraycopy( data[ i ].getHeaderID().getBytes(),
+                              0, result, start, 2 );
+            System.arraycopy( data[ i ].getLocalFileDataLength().getBytes(),
+                              0, result, start + 2, 2 );
+            byte[] local = data[ i ].getLocalFileDataData();
+            System.arraycopy( local, 0, result, start + 4, local.length );
+            start += ( local.length + 4 );
+        }
+        return result;
+    }
+
+    /**
+     * Split the array into ExtraFields and populate them with the give data.
+     *
+     * @param data the data to parse
+     * @return the parsed fields
+     * @exception ZipException on error
+     * @since 1.1
+     */
+    public static ZipExtraField[] parse( final byte[] data )
+        throws ZipException
+    {
+        ArrayList v = new ArrayList();
+        int start = 0;
+        while( start <= data.length - 4 )
+        {
+            final 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.add( 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" ); +        }
+
+        final ZipExtraField[] result = new ZipExtraField[ v.size() ];
+        return (ZipExtraField[])v.toArray( result );
+    }
+
+    /**
+     * Register a ZipExtraField implementation. <p>
+     *
+     * The given class must have a no-arg constructor and implement the {@link
+     * ZipExtraField ZipExtraField interface}.</p>
+     *
+     * @param clazz The Class for particular implementation
+     * @since 1.1
+     */
+    public static void register( final Class clazz )
+    {
+        try
+        {
+            ZipExtraField ze = (ZipExtraField)clazz.newInstance();
+            c_implementations.put( ze.getHeaderID(), clazz );
+        }
+        catch( ClassCastException cc )
+        {
+            throw new RuntimeException( clazz +
+                                        " doesn\'t implement ZipExtraField" );
+        }
+        catch( InstantiationException ie )
+        {
+            throw new RuntimeException( clazz + " is not a concrete class" );
+        }
+        catch( IllegalAccessException ie )
+        {
+            throw new RuntimeException( clazz +
+                                        "\'s no-arg constructor is not public" );
+        }
+    }
+}

Propchange: jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtils.java
                
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtils.java
                
------------------------------------------------------------------------------
    svn:executable = *

Propchange: jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/zip/ExtraFieldUtils.java
                
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"

Added: jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/zip/UnixStat.java
                
URL: http://svn.apache.org/viewvc/jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/zip/UnixStat.java?rev=427072&view=auto
 ==============================================================================
--- jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/zip/UnixStat.java \
                (added)
+++ jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/zip/UnixStat.java \
Mon Jul 31 03:55:10 2006 @@ -0,0 +1,76 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.compress.archivers.zip;
+
+/**
+ * Constants from stat.h on Unix systems.
+ *
+ * @author <a href="stefan.bodewig@epost.de">Stefan Bodewig</a>
+ * @version $Revision: 155439 $
+ */
+public interface UnixStat
+{
+    /**
+     * Bits used for permissions (and sticky bit)
+     *
+     * @since 1.1
+     */
+    int PERM_MASK = 07777;
+    /**
+     * Indicates symbolic links.
+     *
+     * @since 1.1
+     */
+    int LINK_FLAG = 0120000;
+    /**
+     * Indicates plain files.
+     *
+     * @since 1.1
+     */
+    int FILE_FLAG = 0100000;
+    /**
+     * Indicates directories.
+     *
+     * @since 1.1
+     */
+    int DIR_FLAG = 040000;
+
+    // ----------------------------------------------------------
+    // somewhat arbitrary choices that are quite common for shared
+    // installations
+    // -----------------------------------------------------------
+
+    /**
+     * Default permissions for symbolic links.
+     *
+     * @since 1.1
+     */
+    int DEFAULT_LINK_PERM = 0777;
+
+    /**
+     * Default permissions for directories.
+     *
+     * @since 1.1
+     */
+    int DEFAULT_DIR_PERM = 0755;
+
+    /**
+     * Default permissions for plain files.
+     *
+     * @since 1.1
+     */
+    int DEFAULT_FILE_PERM = 0644;
+}

Propchange: jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/zip/UnixStat.java
                
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/zip/UnixStat.java
                
------------------------------------------------------------------------------
    svn:executable = *

Propchange: jakarta/commons/sandbox/compress/trunk/src/java/org/apache/commons/compress/archivers/zip/UnixStat.java
                
------------------------------------------------------------------------------
    svn:keywords = "Author Date Id Revision"



---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic