commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From tcu...@apache.org
Subject svn commit: r674372 [2/6] - in /commons/sandbox/compress/branches/redesign: ./ src/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/commons/ src/main/java/org/apache/commons/compress/ src/main/java/org/apa...
Date Mon, 07 Jul 2008 00:28:49 GMT
Added: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveEntry.java
URL: http://svn.apache.org/viewvc/commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveEntry.java?rev=674372&view=auto
==============================================================================
--- commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveEntry.java (added)
+++ commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveEntry.java Sun Jul  6 17:28:46 2008
@@ -0,0 +1,692 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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;
+
+import org.apache.commons.compress.archivers.ArchiveEntry;
+
+/**
+ * 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: 437550 $ $Date: 2006-08-28 08:10:02 +0200 (Mo, 28 Aug 2006) $
+ * @see TarInputStream
+ * @see TarArchiveOutputStream
+ */
+public class TarArchiveEntry implements ArchiveEntry {
+    /**
+     * 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 TarArchiveEntry( final String name )
+    {
+        this();
+
+        final boolean isDir = name.endsWith( "/" );
+
+        m_name = new StringBuffer( name );
+        m_mode = isDir ? 040755 : 0100644;
+        m_linkFlag = isDir ? TarConstants.LF_DIR : TarConstants.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 TarArchiveEntry( 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 TarArchiveEntry( 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 = TarConstants.LF_DIR;
+
+            if( m_name.charAt( m_name.length() - 1 ) != '/' )
+            {
+                m_name.append( "/" );
+            }
+        }
+        else
+        {
+            m_mode = 0100644;
+            m_linkFlag = TarConstants.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 TarArchiveEntry( final byte[] header )
+    {
+        this();
+        parseTarHeader( header );
+    }
+
+    /**
+     * Construct an empty entry and prepares the header values.
+     */
+    private TarArchiveEntry()
+    {
+        m_magic = new StringBuffer( TarConstants.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 TarArchiveEntry[] getDirectoryEntries()
+    {
+        if( null == m_file || !m_file.isDirectory() )
+        {
+            return new TarArchiveEntry[ 0 ];
+        }
+
+        final String[] list = m_file.list();
+        final TarArchiveEntry[] result = new TarArchiveEntry[ list.length ];
+
+        for( int i = 0; i < list.length; ++i )
+        {
+            result[ i ] = new TarArchiveEntry( 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 TarArchiveEntry 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 == TarConstants.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 == TarConstants.LF_GNUTYPE_LONGNAME &&
+            m_name.toString().equals( TarConstants.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 TarArchiveEntry 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, TarConstants.MODELEN );
+        offset += TarConstants.MODELEN;
+        m_userID = (int)TarUtils.parseOctal( header, offset, TarConstants.UIDLEN );
+        offset += TarConstants.UIDLEN;
+        m_groupID = (int)TarUtils.parseOctal( header, offset, TarConstants.GIDLEN );
+        offset += TarConstants.GIDLEN;
+        m_size = TarUtils.parseOctal( header, offset, TarConstants.SIZELEN );
+        offset += TarConstants.SIZELEN;
+        m_modTime = TarUtils.parseOctal( header, offset, TarConstants.MODTIMELEN );
+        offset += TarConstants.MODTIMELEN;
+        m_checkSum = (int)TarUtils.parseOctal( header, offset, TarConstants.CHKSUMLEN );
+        offset += TarConstants.CHKSUMLEN;
+        m_linkFlag = header[ offset++ ];
+        m_linkName = TarUtils.parseName( header, offset, NAMELEN );
+        offset += NAMELEN;
+        m_magic = TarUtils.parseName( header, offset, TarConstants.MAGICLEN );
+        offset += TarConstants.MAGICLEN;
+        m_userName = TarUtils.parseName( header, offset, TarConstants.UNAMELEN );
+        offset += TarConstants.UNAMELEN;
+        m_groupName = TarUtils.parseName( header, offset, TarConstants.GNAMELEN );
+        offset += TarConstants.GNAMELEN;
+        m_devMajor = (int)TarUtils.parseOctal( header, offset, TarConstants.DEVLEN );
+        offset += TarConstants.DEVLEN;
+        m_devMinor = (int)TarUtils.parseOctal( header, offset, TarConstants.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, TarConstants.MODELEN );
+        offset = TarUtils.getOctalBytes( m_userID, buffer, offset, TarConstants.UIDLEN );
+        offset = TarUtils.getOctalBytes( m_groupID, buffer, offset, TarConstants.GIDLEN );
+        offset = TarUtils.getLongOctalBytes( m_size, buffer, offset, TarConstants.SIZELEN );
+        offset = TarUtils.getLongOctalBytes( m_modTime, buffer, offset, TarConstants.MODTIMELEN );
+
+        final int checkSumOffset = offset;
+        for( int i = 0; i < TarConstants.CHKSUMLEN; ++i )
+        {
+            buffer[ offset++ ] = (byte)' ';
+        }
+
+        buffer[ offset++ ] = m_linkFlag;
+        offset = TarUtils.getNameBytes( m_linkName, buffer, offset, NAMELEN );
+        offset = TarUtils.getNameBytes( m_magic, buffer, offset, TarConstants.MAGICLEN );
+        offset = TarUtils.getNameBytes( m_userName, buffer, offset, TarConstants.UNAMELEN );
+        offset = TarUtils.getNameBytes( m_groupName, buffer, offset, TarConstants.GNAMELEN );
+        offset = TarUtils.getOctalBytes( m_devMajor, buffer, offset, TarConstants.DEVLEN );
+        offset = TarUtils.getOctalBytes( m_devMinor, buffer, offset, TarConstants.DEVLEN );
+
+        while( offset < buffer.length )
+        {
+            buffer[ offset++ ] = 0;
+        }
+
+        final long checkSum = TarUtils.computeCheckSum( buffer );
+        TarUtils.getCheckSumOctalBytes( checkSum, buffer, checkSumOffset, TarConstants.CHKSUMLEN );
+    }
+}

Added: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java
URL: http://svn.apache.org/viewvc/commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java?rev=674372&view=auto
==============================================================================
--- commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java (added)
+++ commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveInputStream.java Sun Jul  6 17:28:46 2008
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.IOException;
+import java.io.InputStream;
+
+import org.apache.commons.compress.archivers.ArchiveEntry;
+import org.apache.commons.compress.archivers.ArchiveInputStream;
+
+public class TarArchiveInputStream extends ArchiveInputStream {
+    private final TarInputStream in;
+    
+	public TarArchiveInputStream( InputStream inputStream ) {
+		in = new TarInputStream(inputStream);
+	}
+
+    public ArchiveEntry getNextEntry() throws IOException {
+        return (ArchiveEntry)in.getNextEntry();
+    }
+
+    public int read(byte[] b, int off, int len) throws IOException {
+        return in.read(b, off, len);
+    }
+
+    public int read() throws IOException {
+        return in.read();
+    }
+    
+    public static boolean matches( byte[] signature ) {
+    	// 6574 7473 2e31 6d78
+    	
+    	if (signature[0] != 0x74) {
+    		return false;
+    	}
+    	if (signature[1] != 0x65) {
+    		return false;
+    	}
+    	if (signature[2] != 0x73) {
+    		return false;
+    	}
+    	if (signature[3] != 0x74) {
+    		return false;
+    	}
+    	if (signature[4] != 0x31) {
+    		return false;
+    	}
+    	if (signature[5] != 0x2e) {
+    		return false;
+    	}
+    	if (signature[6] != 0x78) {
+    		return false;
+    	}
+    	if (signature[7] != 0x6d) {
+    		return false;
+    	}
+    	
+    	return true;
+    }
+    
+}

Added: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStream.java
URL: http://svn.apache.org/viewvc/commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStream.java?rev=674372&view=auto
==============================================================================
--- commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStream.java (added)
+++ commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarArchiveOutputStream.java Sun Jul  6 17:28:46 2008
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.IOException;
+import java.io.OutputStream;
+
+import org.apache.commons.compress.archivers.ArchiveEntry;
+import org.apache.commons.compress.archivers.ArchiveOutputStream;
+
+public class TarArchiveOutputStream extends ArchiveOutputStream {
+
+    private TarOutputStream out = null;
+    
+    /**
+     * @param out
+     */
+    public TarArchiveOutputStream(OutputStream out) {
+        this.out = new TarOutputStream(out);
+    }
+    
+    public void close() throws IOException {
+        this.out.close();
+    }
+
+    public void closeArchiveEntry() throws IOException {
+        this.out.closeEntry();
+    }
+
+    public void putArchiveEntry(ArchiveEntry entry) throws IOException {
+        this.out.putNextEntry((TarArchiveEntry)entry);
+    }
+
+    public void write(byte[] buffer, int offset, int length) throws IOException {
+        this.out.write(buffer, offset, length);
+    }
+
+    public String getDefaultFileExtension() {
+        return "tar";
+    }
+
+    public byte[] getHeader() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public String getName() {
+        return "tar";
+    }
+
+    public void write(int b) throws IOException {
+        this.out.write(b);
+    }
+}
+

Added: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarBuffer.java
URL: http://svn.apache.org/viewvc/commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarBuffer.java?rev=674372&view=auto
==============================================================================
--- commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarBuffer.java (added)
+++ commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarBuffer.java Sun Jul  6 17:28:46 2008
@@ -0,0 +1,509 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+
+/**
+ * The TarBuffer class implements the tar archive concept of a buffered input
+ * stream. This concept goes back to the days of blocked tape drives and special
+ * io devices. In the Java universe, the only real function that this class
+ * performs is to ensure that files have the correct "block" size, or other tars
+ * will complain. <p>
+ *
+ * You should never have a need to access this class directly. TarBuffers are
+ * created by Tar IO Streams.
+ *
+ * @author <a href="mailto:time@ice.com">Timothy Gerard Endres</a>
+ * @author <a href="mailto:peter@apache.org">Peter Donald</a>
+ * @version $Revision: 437550 $ $Date: 2006-08-28 08:10:02 +0200 (Mo, 28 Aug 2006) $
+ */
+class TarBuffer
+{
+    public static final int DEFAULT_RECORDSIZE = ( 512 );
+    public static final int DEFAULT_BLOCKSIZE = ( DEFAULT_RECORDSIZE * 20 );
+
+    private byte[] m_blockBuffer;
+    private int m_blockSize;
+    private int m_currBlkIdx;
+    private int m_currRecIdx;
+    private boolean m_debug;
+
+    private InputStream m_input;
+    private OutputStream m_output;
+    private int m_recordSize;
+    private int m_recsPerBlock;
+
+    public TarBuffer( final InputStream input )
+    {
+        this( input, TarBuffer.DEFAULT_BLOCKSIZE );
+    }
+
+    public TarBuffer( final InputStream input, final int blockSize )
+    {
+        this( input, blockSize, TarBuffer.DEFAULT_RECORDSIZE );
+    }
+
+    public TarBuffer( final InputStream input,
+                      final int blockSize,
+                      final int recordSize )
+    {
+        m_input = input;
+        initialize( blockSize, recordSize );
+    }
+
+    public TarBuffer( final OutputStream output )
+    {
+        this( output, TarBuffer.DEFAULT_BLOCKSIZE );
+    }
+
+    public TarBuffer( final OutputStream output, final int blockSize )
+    {
+        this( output, blockSize, TarBuffer.DEFAULT_RECORDSIZE );
+    }
+
+    public TarBuffer( final OutputStream output,
+                      final int blockSize,
+                      final int recordSize )
+    {
+        m_output = output;
+        initialize( blockSize, recordSize );
+    }
+
+    /**
+     * Set the debugging flag for the buffer.
+     *
+     * @param debug If true, print debugging output.
+     */
+    public void setDebug( final boolean debug )
+    {
+        m_debug = debug;
+    }
+
+    /**
+     * Get the TAR Buffer's block size. Blocks consist of multiple records.
+     *
+     * @return The BlockSize value
+     */
+    public int getBlockSize()
+    {
+        return m_blockSize;
+    }
+
+    /**
+     * Get the current block number, zero based.
+     *
+     * @return The current zero based block number.
+     */
+    public int getCurrentBlockNum()
+    {
+        return m_currBlkIdx;
+    }
+
+    /**
+     * Get the current record number, within the current block, zero based.
+     * Thus, current offset = (currentBlockNum * recsPerBlk) + currentRecNum.
+     *
+     * @return The current zero based record number.
+     */
+    public int getCurrentRecordNum()
+    {
+        return m_currRecIdx - 1;
+    }
+
+    /**
+     * Get the TAR Buffer's record size.
+     *
+     * @return The RecordSize value
+     */
+    public int getRecordSize()
+    {
+        return m_recordSize;
+    }
+
+    /**
+     * Determine if an archive record indicate End of Archive. End of archive is
+     * indicated by a record that consists entirely of null bytes.
+     *
+     * @param record The record data to check.
+     * @return The EOFRecord value
+     */
+    public boolean isEOFRecord( final byte[] record )
+    {
+        final int size = getRecordSize();
+        for( int i = 0; i < size; ++i )
+        {
+            if( record[ i ] != 0 )
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Close the TarBuffer. If this is an output buffer, also flush the current
+     * block before closing.
+     */
+    public void close()
+        throws IOException
+    {
+        if( m_debug )
+        {
+            debug( "TarBuffer.closeBuffer()." );
+        }
+
+        if( null != m_output )
+        {
+            flushBlock();
+
+            if( m_output != System.out && m_output != System.err )
+            {
+                m_output.close();
+                m_output = null;
+            }
+        }
+        else if( m_input != null )
+        {
+            if( m_input != System.in )
+            {
+                m_input.close();
+                m_input = null;
+            }
+        }
+    }
+
+    /**
+     * Read a record from the input stream and return the data.
+     *
+     * @return The record data.
+     * @exception IOException Description of Exception
+     */
+    public byte[] readRecord()
+        throws IOException
+    {
+        if( m_debug )
+        {
+            final String message = "ReadRecord: recIdx = " + m_currRecIdx +
+                " blkIdx = " + m_currBlkIdx;
+            debug( message );
+        }
+
+        if( null == m_input )
+        {
+            final String message = "reading from an output buffer";
+            throw new IOException( message );
+        }
+
+        if( m_currRecIdx >= m_recsPerBlock )
+        {
+            if( !readBlock() )
+            {
+                return null;
+            }
+        }
+
+        final byte[] result = new byte[ m_recordSize ];
+        System.arraycopy( m_blockBuffer,
+                          ( m_currRecIdx * m_recordSize ),
+                          result,
+                          0,
+                          m_recordSize );
+
+        m_currRecIdx++;
+
+        return result;
+    }
+
+    /**
+     * Skip over a record on the input stream.
+     */
+    public void skipRecord()
+        throws IOException
+    {
+        if( m_debug )
+        {
+            final String message = "SkipRecord: recIdx = " + m_currRecIdx +
+                " blkIdx = " + m_currBlkIdx;
+            debug( message );
+        }
+
+        if( null == m_input )
+        {
+            final String message = "reading (via skip) from an output buffer";
+            throw new IOException( message );
+        }
+
+        if( m_currRecIdx >= m_recsPerBlock )
+        {
+            if( !readBlock() )
+            {
+                return;// UNDONE
+            }
+        }
+
+        m_currRecIdx++;
+    }
+
+    /**
+     * Write an archive record to the archive.
+     *
+     * @param record The record data to write to the archive.
+     */
+    public void writeRecord( final byte[] record )
+        throws IOException
+    {
+        if( m_debug )
+        {
+            final String message = "WriteRecord: recIdx = " + m_currRecIdx +
+                " blkIdx = " + m_currBlkIdx;
+            debug( message );
+        }
+
+        if( null == m_output )
+        {
+            final String message = "writing to an input buffer";
+            throw new IOException( message );
+        }
+
+        if( record.length != m_recordSize )
+        {
+            final String message = "record to write has length '" +
+                record.length + "' which is not the record size of '" +
+                m_recordSize + "'";
+            throw new IOException( message );
+        }
+
+        if( m_currRecIdx >= m_recsPerBlock )
+        {
+            writeBlock();
+        }
+
+        System.arraycopy( record,
+                          0,
+                          m_blockBuffer,
+                          ( m_currRecIdx * m_recordSize ),
+                          m_recordSize );
+
+        m_currRecIdx++;
+    }
+
+    /**
+     * Write an archive record to the archive, where the record may be inside of
+     * a larger array buffer. The buffer must be "offset plus record size" long.
+     *
+     * @param buffer The buffer containing the record data to write.
+     * @param offset The offset of the record data within buf.
+     */
+    public void writeRecord( final byte[] buffer, final int offset )
+        throws IOException
+    {
+        if( m_debug )
+        {
+            final String message = "WriteRecord: recIdx = " + m_currRecIdx +
+                " blkIdx = " + m_currBlkIdx;
+            debug( message );
+        }
+
+        if( null == m_output )
+        {
+            final String message = "writing to an input buffer";
+            throw new IOException( message );
+        }
+
+        if( ( offset + m_recordSize ) > buffer.length )
+        {
+            final String message = "record has length '" + buffer.length +
+                "' with offset '" + offset + "' which is less than the record size of '" +
+                m_recordSize + "'";
+            throw new IOException( message );
+        }
+
+        if( m_currRecIdx >= m_recsPerBlock )
+        {
+            writeBlock();
+        }
+
+        System.arraycopy( buffer,
+                          offset,
+                          m_blockBuffer,
+                          ( m_currRecIdx * m_recordSize ),
+                          m_recordSize );
+
+        m_currRecIdx++;
+    }
+
+    /**
+     * Flush the current data block if it has any data in it.
+     */
+    private void flushBlock()
+        throws IOException
+    {
+        if( m_debug )
+        {
+            final String message = "TarBuffer.flushBlock() called.";
+            debug( message );
+        }
+
+        if( m_output == null )
+        {
+            final String message = "writing to an input buffer";
+            throw new IOException( message );
+        }
+
+        if( m_currRecIdx > 0 )
+        {
+            writeBlock();
+        }
+    }
+
+    /**
+     * Initialization common to all constructors.
+     */
+    private void initialize( final int blockSize, final int recordSize )
+    {
+        m_debug = false;
+        m_blockSize = blockSize;
+        m_recordSize = recordSize;
+        m_recsPerBlock = ( m_blockSize / m_recordSize );
+        m_blockBuffer = new byte[ m_blockSize ];
+
+        if( null != m_input )
+        {
+            m_currBlkIdx = -1;
+            m_currRecIdx = m_recsPerBlock;
+        }
+        else
+        {
+            m_currBlkIdx = 0;
+            m_currRecIdx = 0;
+        }
+    }
+
+    /**
+     * @return false if End-Of-File, else true
+     */
+    private boolean readBlock()
+        throws IOException
+    {
+        if( m_debug )
+        {
+            final String message = "ReadBlock: blkIdx = " + m_currBlkIdx;
+            debug( message );
+        }
+
+        if( null == m_input )
+        {
+            final String message = "reading from an output buffer";
+            throw new IOException( message );
+        }
+
+        m_currRecIdx = 0;
+
+        int offset = 0;
+        int bytesNeeded = m_blockSize;
+
+        while( bytesNeeded > 0 )
+        {
+            final long numBytes = m_input.read( m_blockBuffer, offset, bytesNeeded );
+
+            //
+            // NOTE
+            // We have fit EOF, and the block is not full!
+            //
+            // This is a broken archive. It does not follow the standard
+            // blocking algorithm. However, because we are generous, and
+            // it requires little effort, we will simply ignore the error
+            // and continue as if the entire block were read. This does
+            // not appear to break anything upstream. We used to return
+            // false in this case.
+            //
+            // Thanks to 'Yohann.Roussel@alcatel.fr' for this fix.
+            //
+            if( numBytes == -1 )
+            {
+                // However, just leaving the unread portion of the buffer dirty does
+                // cause problems in some cases.  This problem is described in
+                // http://issues.apache.org/bugzilla/show_bug.cgi?id=29877
+                //
+                // The solution is to fill the unused portion of the buffer with zeros.
+
+                Arrays.fill(m_blockBuffer, offset, offset + bytesNeeded, (byte) 0);
+
+                break;
+            }
+
+            offset += numBytes;
+            bytesNeeded -= numBytes;
+
+            if( numBytes != m_blockSize )
+            {
+                if( m_debug )
+                {
+                    System.err.println( "ReadBlock: INCOMPLETE READ "
+                                        + numBytes + " of " + m_blockSize
+                                        + " bytes read." );
+                }
+            }
+        }
+
+        m_currBlkIdx++;
+
+        return true;
+    }
+
+    /**
+     * Write a TarBuffer block to the archive.
+     *
+     * @exception IOException Description of Exception
+     */
+    private void writeBlock()
+        throws IOException
+    {
+        if( m_debug )
+        {
+            final String message = "WriteBlock: blkIdx = " + m_currBlkIdx;
+            debug( message );
+        }
+
+        if( null == m_output )
+        {
+            final String message = "writing to an input buffer";
+            throw new IOException( message );
+        }
+
+        m_output.write( m_blockBuffer, 0, m_blockSize );
+        m_output.flush();
+
+        m_currRecIdx = 0;
+        m_currBlkIdx++;
+    }
+
+    protected void debug( final String message )
+    {
+        if( m_debug )
+        {
+            System.err.println( message );
+        }
+    }
+}

Added: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarConstants.java
URL: http://svn.apache.org/viewvc/commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarConstants.java?rev=674372&view=auto
==============================================================================
--- commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarConstants.java (added)
+++ commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarConstants.java Sun Jul  6 17:28:46 2008
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 interface contains all the definitions used in the package.
+ *
+ * @author <a href="mailto:time@ice.com">Timothy Gerard Endres</a>
+ * @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a>
+ * @version $Revision: 437550 $ $Date: 2006-08-28 08:10:02 +0200 (Mo, 28 Aug 2006) $
+ */
+interface TarConstants
+{
+    /**
+     * The length of the mode field in a header buffer.
+     */
+    int MODELEN = 8;
+
+    /**
+     * The length of the user id field in a header buffer.
+     */
+    int UIDLEN = 8;
+
+    /**
+     * The length of the group id field in a header buffer.
+     */
+    int GIDLEN = 8;
+
+    /**
+     * The length of the checksum field in a header buffer.
+     */
+    int CHKSUMLEN = 8;
+
+    /**
+     * The length of the size field in a header buffer.
+     */
+    int SIZELEN = 12;
+
+    /**
+     * The length of the magic field in a header buffer.
+     */
+    int MAGICLEN = 8;
+
+    /**
+     * The length of the modification time field in a header buffer.
+     */
+    int MODTIMELEN = 12;
+
+    /**
+     * The length of the user name field in a header buffer.
+     */
+    int UNAMELEN = 32;
+
+    /**
+     * The length of the group name field in a header buffer.
+     */
+    int GNAMELEN = 32;
+
+    /**
+     * The length of the devices field in a header buffer.
+     */
+    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.
+     */
+    byte LF_OLDNORM = 0;
+
+    /**
+     * Normal file type.
+     */
+    byte LF_NORMAL = (byte)'0';
+
+    /**
+     * Link file type.
+     */
+    byte LF_LINK = (byte)'1';
+
+    /**
+     * Symbolic link file type.
+     */
+    byte LF_SYMLINK = (byte)'2';
+
+    /**
+     * Character device file type.
+     */
+    byte LF_CHR = (byte)'3';
+
+    /**
+     * Block device file type.
+     */
+    byte LF_BLK = (byte)'4';
+
+    /**
+     * Directory file type.
+     */
+    byte LF_DIR = (byte)'5';
+
+    /**
+     * FIFO (pipe) file type.
+     */
+    byte LF_FIFO = (byte)'6';
+
+    /**
+     * Contiguous file type.
+     */
+    byte LF_CONTIG = (byte)'7';
+
+    /**
+     * The magic tag representing a POSIX tar archive.
+     */
+    String TMAGIC = "ustar";
+
+    /**
+     * The magic tag representing a GNU tar archive.
+     */
+    String GNU_TMAGIC = "ustar  ";
+
+    /**
+     * The namr of the GNU tar entry which contains a long name.
+     */
+    String GNU_LONGLINK = "././@LongLink";
+
+    /**
+     * Identifies the *next* file on the tape as having a long name.
+     */
+    byte LF_GNUTYPE_LONGNAME = (byte)'L';
+}

Added: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarInputStream.java
URL: http://svn.apache.org/viewvc/commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarInputStream.java?rev=674372&view=auto
==============================================================================
--- commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarInputStream.java (added)
+++ commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarInputStream.java Sun Jul  6 17:28:46 2008
@@ -0,0 +1,475 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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: 437550 $ $Date: 2006-08-28 08:10:02 +0200 (Mo, 28 Aug 2006) $
+ * @see TarInputStream
+ * @see TarArchiveEntry
+ */
+public class TarInputStream
+    extends FilterInputStream
+{
+    private TarBuffer m_buffer;
+    private TarArchiveEntry 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 TarArchiveEntry 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 TarArchiveEntry( 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 );
+        }
+    }
+}

Added: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarOutputStream.java
URL: http://svn.apache.org/viewvc/commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarOutputStream.java?rev=674372&view=auto
==============================================================================
--- commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarOutputStream.java (added)
+++ commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarOutputStream.java Sun Jul  6 17:28:46 2008
@@ -0,0 +1,427 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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: 437550 $ $Date: 2006-08-28 08:10:02 +0200 (Mo, 28 Aug 2006) $
+ * @see TarInputStream
+ * @see TarArchiveEntry
+ */
+public 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 TarArchiveEntry to be written to the archive.
+     * @exception IOException when an IO error causes operation to fail
+     */
+    public void putNextEntry( final TarArchiveEntry entry )
+        throws IOException
+    {
+        if( entry.getName().length() >= TarArchiveEntry.NAMELEN )
+        {
+            if( m_longFileMode == LONGFILE_GNU )
+            {
+                // create a TarArchiveEntry for the LongLink, the contents
+                // of which are the entry's name
+                final TarArchiveEntry longLinkEntry =
+                    new TarArchiveEntry( TarConstants.GNU_LONGLINK,
+                                  TarConstants.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 ( > " + TarArchiveEntry.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
+     */
+    public 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 );
+    }
+}

Added: commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarUtils.java
URL: http://svn.apache.org/viewvc/commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarUtils.java?rev=674372&view=auto
==============================================================================
--- commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarUtils.java (added)
+++ commons/sandbox/compress/branches/redesign/src/main/java/org/apache/commons/compress/archivers/tar/TarUtils.java Sun Jul  6 17:28:46 2008
@@ -0,0 +1,236 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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: 437550 $ $Date: 2006-08-28 08:10:02 +0200 (Mo, 28 Aug 2006) $
+ */
+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;
+    }
+}



Mime
View raw message