TarOutputStream writes a UNIX tar archive as an OutputStream : Zip Tar File « File Input Output « Java

Java
1. 2D Graphics GUI
2. 3D
3. Advanced Graphics
4. Ant
5. Apache Common
6. Chart
7. Class
8. Collections Data Structure
9. Data Type
10. Database SQL JDBC
11. Design Pattern
12. Development Class
13. EJB3
14. Email
15. Event
16. File Input Output
17. Game
18. Generics
19. GWT
20. Hibernate
21. I18N
22. J2EE
23. J2ME
24. JDK 6
25. JNDI LDAP
26. JPA
27. JSP
28. JSTL
29. Language Basics
30. Network Protocol
31. PDF RTF
32. Reflection
33. Regular Expressions
34. Scripting
35. Security
36. Servlets
37. Spring
38. Swing Components
39. Swing JFC
40. SWT JFace Eclipse
41. Threads
42. Tiny Application
43. Velocity
44. Web Services SOA
45. XML
Java Tutorial
Java Source Code / Java Documentation
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java » File Input Output » Zip Tar FileScreenshots 
TarOutputStream writes a UNIX tar archive as an OutputStream
     
/*
** Authored by Timothy Gerard Endres
** <mailto:time@gjt.org>  <http://www.trustice.com>
** 
** This work has been placed into the public domain.
** You may use this work in any way and for any purpose you wish.
**
** THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND,
** NOT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR
** OF THIS SOFTWARE, ASSUMES _NO_ RESPONSIBILITY FOR ANY
** CONSEQUENCE RESULTING FROM THE USE, MODIFICATION, OR
** REDISTRIBUTION OF THIS SOFTWARE. 
** 
*/

import java.io.File;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;


/**
 * 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().
 *
 *
 @version $Revision: 12504 $
 @author Timothy Gerard Endres,
 *  <a href="mailto:time@gjt.org">time@trustice.com</a>.
 @see TarBuffer
 @see TarHeader
 @see TarEntry
 */


public
class   TarOutputStream
extends   FilterOutputStream
  {
  protected boolean     debug;
  protected int       currSize;
  protected int       currBytes;
  protected byte[]      oneBuf;
  protected byte[]      recordBuf;
  protected int       assemLen;
  protected byte[]      assemBuf;
  protected TarBuffer     buffer;


  public
  TarOutputStreamOutputStream os )
    {
    thisos, TarBuffer.DEFAULT_BLKSIZE, TarBuffer.DEFAULT_RCDSIZE );
    }

  public
  TarOutputStreamOutputStream os, int blockSize )
    {
    thisos, blockSize, TarBuffer.DEFAULT_RCDSIZE );
    }

  public
  TarOutputStreamOutputStream os, int blockSize, int recordSize )
    {
    superos );

    this.buffer = new TarBufferos, blockSize, recordSize );
    
    this.debug = false;
    this.assemLen = 0;
    this.assemBuf = new byterecordSize ];
    this.recordBuf = new byterecordSize ];
    this.oneBuf = new byte[1];
    }

  /**
   * Sets the debugging flag.
   *
   @param debugF True to turn on debugging.
   */
  public void
  setDebugboolean debugF )
    {
    this.debug = debugF;
    }

  /**
   * Sets the debugging flag in this stream's TarBuffer.
   *
   @param debugF True to turn on debugging.
   */
  public void
  setBufferDebugboolean debug )
    {
    this.buffer.setDebugdebug );
    }

  /**
   * Ends the TAR archive without closing the underlying OutputStream.
   * The result is that the EOF record of nulls is written.
   */

  public void
  finish()
    throws IOException
    {
    this.writeEOFRecord();
    }

  /**
   * Ends the TAR archive and closes the underlying OutputStream.
   * This means that finish() is called followed by calling the
   * TarBuffer's close().
   */

  public void
  close()
    throws IOException
    {
    this.finish();
    this.buffer.close();
    }

  /**
   * Get the record size being used by this stream's TarBuffer.
   *
   @return The TarBuffer record size.
   */
  public int
  getRecordSize()
    {
    return this.buffer.getRecordSize();
    }

  /**
   * 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.
   */
  public void
  putNextEntryTarEntry entry )
    throws IOException
    {
    if entry.getHeader().name.length() > TarHeader.NAMELEN )
      throw new InvalidHeaderException
        "file name '" + entry.getHeader().name
          "' is too long ( > "
          + TarHeader.NAMELEN + " bytes )" );

    entry.writeEntryHeaderthis.recordBuf );
    this.buffer.writeRecordthis.recordBuf );

    this.currBytes = 0;

    if entry.isDirectory() )
      this.currSize = 0;
    else
      this.currSize = (int)entry.getSize();
    }

  /**
   * 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.
   */
  public void
  closeEntry()
    throws IOException
    {
    if this.assemLen > )
      {
      for int i = this.assemLen ; i < this.assemBuf.length ; ++i )
        this.assemBuf[i0;

      this.buffer.writeRecordthis.assemBuf );

      this.currBytes += this.assemLen;
      this.assemLen = 0;
      }

    if this.currBytes < this.currSize )
      throw new IOException
        "entry closed at '" this.currBytes
          "' before the '" this.currSize
          "' bytes specified in the header were written" );
    }

  /**
   * Writes a byte to the current tar archive entry.
   *
   * This method simply calls read( byte[], int, int ).
   *
   @param b The byte written.
   */
  public void
  writeint )
    throws IOException
    {
    this.oneBuf[0(byteb;
    this.writethis.oneBuf, 0);
    }

  /**
   * Writes bytes to the current tar archive entry.
   *
   * This method simply calls read( byte[], int, int ).
   *
   @param wBuf The buffer to write to the archive.
   @return The number of bytes read, or -1 at EOF.
   */
  public void
  writebyte[] wBuf )
    throws IOException
    {
    this.writewBuf, 0, wBuf.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.
   *
   * This method simply calls read( byte[], int, int ).
   *
   @param wBuf The buffer to write to the archive.
   @param wOffset The offset in the buffer from which to get bytes.
   @param numToWrite The number of bytes to write.
   */
  public void
  writebyte[] wBuf, int wOffset, int numToWrite )
    throws IOException
    {
    if ( (this.currBytes + numToWritethis.currSize )
      throw new IOException
        "request to write '" + numToWrite
          "' bytes exceeds size in header of '"
          this.currSize + "' bytes" );

    //
    // 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 this.assemLen > )
      {
      if ( (this.assemLen + numToWrite >= this.recordBuf.length )
        {
        int aLen = this.recordBuf.length - this.assemLen;

        System.arraycopy
          this.assemBuf, 0this.recordBuf, 0this.assemLen );

        System.arraycopy
          wBuf, wOffset, this.recordBuf, this.assemLen, aLen );

        this.buffer.writeRecordthis.recordBuf );

        this.currBytes += this.recordBuf.length;

        wOffset += aLen;
        numToWrite -= aLen;
        this.assemLen = 0;
        }
      else // ( (this.assemLen + numToWrite ) < this.recordBuf.length )
        {
        System.arraycopy
          wBuf, wOffset, this.assemBuf,
            this.assemLen, numToWrite );
        wOffset += numToWrite;
        this.assemLen += numToWrite; 
        numToWrite -= numToWrite;
        }
      }

    //
    // When we get here we have EITHER:
    //   o An empty "assemble" buffer.
    //   o No bytes to write (numToWrite == 0)
    //

    for ; numToWrite > )
      {
      if numToWrite < this.recordBuf.length )
        {
        System.arraycopy
          wBuf, wOffset, this.assemBuf, this.assemLen, numToWrite );
        this.assemLen += numToWrite;
        break;
        }

      this.buffer.writeRecordwBuf, wOffset );

      int num = this.recordBuf.length;
      this.currBytes += num;
      numToWrite -= num;
      wOffset += num;
      }
    }

  /**
   * Write an EOF (end of archive) record to the tar archive.
   * An EOF record consists of a record of all zeros.
   */
  private void
  writeEOFRecord()
    throws IOException
    {
    for int i = ; i < this.recordBuf.length ; ++i )
      this.recordBuf[i0;
    this.buffer.writeRecordthis.recordBuf );
    }

  }



/*
 * * Authored by Timothy Gerard Endres * <mailto:time@gjt.org>
 * <http://www.trustice.com> * * This work has been placed into the public
 * domain. * You may use this work in any way and for any purpose you wish. * *
 * THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND, * NOT EVEN THE
 * IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR * OF THIS SOFTWARE, ASSUMES
 * _NO_ RESPONSIBILITY FOR ANY * CONSEQUENCE RESULTING FROM THE USE,
 * MODIFICATION, OR * REDISTRIBUTION OF THIS SOFTWARE. *
 */

/**
 * This class encapsulates the Tar Entry Header used in Tar Archives. The class
 * also holds a number of tar constants, used mostly in headers.
 */

class TarHeader extends Object {
  /**
   * The length of the name field in a header buffer.
   */
  public static final int NAMELEN = 100;

  /**
   * The length of the mode field in a header buffer.
   */
  public static final int MODELEN = 8;

  /**
   * The length of the user id field in a header buffer.
   */
  public static final int UIDLEN = 8;

  /**
   * The length of the group id field in a header buffer.
   */
  public static final int GIDLEN = 8;

  /**
   * The length of the checksum field in a header buffer.
   */
  public static final int CHKSUMLEN = 8;

  /**
   * The length of the size field in a header buffer.
   */
  public static final int SIZELEN = 12;

  /**
   * The length of the magic field in a header buffer.
   */
  public static final int MAGICLEN = 8;

  /**
   * The length of the modification time field in a header buffer.
   */
  public static final int MODTIMELEN = 12;

  /**
   * The length of the user name field in a header buffer.
   */
  public static final int UNAMELEN = 32;

  /**
   * The length of the group name field in a header buffer.
   */
  public static final int GNAMELEN = 32;

  /**
   * The length of the devices field in a header buffer.
   */
  public static final 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.
   */
  public static final byte LF_OLDNORM = 0;

  /**
   * Normal file type.
   */
  public static final byte LF_NORMAL = (byte'0';

  /**
   * Link file type.
   */
  public static final byte LF_LINK = (byte'1';

  /**
   * Symbolic link file type.
   */
  public static final byte LF_SYMLINK = (byte'2';

  /**
   * Character device file type.
   */
  public static final byte LF_CHR = (byte'3';

  /**
   * Block device file type.
   */
  public static final byte LF_BLK = (byte'4';

  /**
   * Directory file type.
   */
  public static final byte LF_DIR = (byte'5';

  /**
   * FIFO (pipe) file type.
   */
  public static final byte LF_FIFO = (byte'6';

  /**
   * Contiguous file type.
   */
  public static final byte LF_CONTIG = (byte'7';

  /**
   * The magic tag representing a POSIX tar archive.
   */
  public static final String TMAGIC = "ustar";

  /**
   * The magic tag representing a GNU tar archive.
   */
  public static final String GNU_TMAGIC = "ustar  ";

  /**
   * The entry's name.
   */
  public StringBuffer name;

  /**
   * The entry's permission mode.
   */
  public int mode;

  /**
   * The entry's user id.
   */
  public int userId;

  /**
   * The entry's group id.
   */
  public int groupId;

  /**
   * The entry's size.
   */
  public long size;

  /**
   * The entry's modification time.
   */
  public long modTime;

  /**
   * The entry's checksum.
   */
  public int checkSum;

  /**
   * The entry's link flag.
   */
  public byte linkFlag;

  /**
   * The entry's link name.
   */
  public StringBuffer linkName;

  /**
   * The entry's magic tag.
   */
  public StringBuffer magic;

  /**
   * The entry's user name.
   */
  public StringBuffer userName;

  /**
   * The entry's group name.
   */
  public StringBuffer groupName;

  /**
   * The entry's major device number.
   */
  public int devMajor;

  /**
   * The entry's minor device number.
   */
  public int devMinor;

  public TarHeader() {
    this.magic = new StringBuffer(TarHeader.TMAGIC);

    this.name = new StringBuffer();
    this.linkName = new StringBuffer();

    String user = System.getProperty("user.name""");

    if (user.length() 31)
      user = user.substring(031);

    this.userId = 0;
    this.groupId = 0;
    this.userName = new StringBuffer(user);
    this.groupName = new StringBuffer("");
  }

  /**
   * TarHeaders can be cloned.
   */
  public Object clone() {
    TarHeader hdr = null;

    try {
      hdr = (TarHeadersuper.clone();

      hdr.name = (this.name == nullnull new StringBuffer(this.name.toString());
      hdr.mode = this.mode;
      hdr.userId = this.userId;
      hdr.groupId = this.groupId;
      hdr.size = this.size;
      hdr.modTime = this.modTime;
      hdr.checkSum = this.checkSum;
      hdr.linkFlag = this.linkFlag;
      hdr.linkName = (this.linkName == nullnull new StringBuffer(this.linkName.toString());
      hdr.magic = (this.magic == nullnull new StringBuffer(this.magic.toString());
      hdr.userName = (this.userName == nullnull new StringBuffer(this.userName.toString());
      hdr.groupName = (this.groupName == nullnull new StringBuffer(this.groupName.toString());
      hdr.devMajor = this.devMajor;
      hdr.devMinor = this.devMinor;
    catch (CloneNotSupportedException ex) {
      ex.printStackTrace();
    }

    return hdr;
  }

  /**
   * Get the name of this entry.
   
   @return Teh entry's name.
   */
  public String getName() {
    return this.name.toString();
  }

  /**
   * 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(byte[] header, int offset, int length)
      throws InvalidHeaderException {
    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;
  }

  /**
   * 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(byte[] header, int offset, int length)
      throws InvalidHeaderException {
    StringBuffer result = new StringBuffer(length);

    int end = offset + length;
    for (int i = offset; i < end; ++i) {
      if (header[i== 0)
        break;
      result.append((charheader[i]);
    }

    return result;
  }

  /**
   * Determine the number of bytes in an entry name.
   
   @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 number of bytes in a header's entry name.
   */
  public static int getNameBytes(StringBuffer name, byte[] buf, int offset, int length) {
    int i;

    for (i = 0; i < length && i < name.length(); ++i) {
      buf[offset + i(bytename.charAt(i);
    }

    for (; i < length; ++i) {
      buf[offset + i0;
    }

    return offset + length;
  }

  /**
   * Parse an octal integer 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 integer value of the octal bytes.
   */
  public static int getOctalBytes(long value, byte[] buf, int offset, int length) {
    byte[] result = new byte[length];

    int idx = length - 1;

    buf[offset + idx0;
    --idx;
    buf[offset + idx(byte' ';
    --idx;

    if (value == 0) {
      buf[offset + idx(byte'0';
      --idx;
    else {
      for (long val = value; idx >= && val > 0; --idx) {
        buf[offset + idx(byte) ((byte'0' (byte) (val & 7));
        val = val >> 3;
      }
    }

    for (; idx >= 0; --idx) {
      buf[offset + idx(byte' ';
    }

    return offset + length;
  }

  /**
   * Parse an octal long integer 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 long value of the octal bytes.
   */
  public static int getLongOctalBytes(long value, byte[] buf, int offset, int length) {
    byte[] temp = new byte[length + 1];
    TarHeader.getOctalBytes(value, temp, 0, length + 1);
    System.arraycopy(temp, 0, buf, offset, length);
    return offset + length;
  }

  /**
   * Parse the checksum octal integer 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 integer value of the entry's checksum.
   */
  public static int getCheckSumOctalBytes(long value, byte[] buf, int offset, int length) {
    TarHeader.getOctalBytes(value, buf, offset, length);
    buf[offset + length - 1(byte' ';
    buf[offset + length - 20;
    return offset + length;
  }

}

/*
 * * Authored by Timothy Gerard Endres * <mailto:time@gjt.org>
 * <http://www.trustice.com> * * This work has been placed into the public
 * domain. * You may use this work in any way and for any purpose you wish. * *
 * THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND, * NOT EVEN THE
 * IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR * OF THIS SOFTWARE, ASSUMES
 * _NO_ RESPONSIBILITY FOR ANY * CONSEQUENCE RESULTING FROM THE USE,
 * MODIFICATION, OR * REDISTRIBUTION OF THIS SOFTWARE. *
 */

/**
 
 * 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>
 
 @see TarHeader
 
 */

class TarEntry extends Object {
  /**
   * If this entry represents a File, this references it.
   */
  protected File file;

  /**
   * This is the entry's header information.
   */
  protected TarHeader header;

  /**
   * Construct an entry with only a name. This allows the programmer to
   * construct the entry's header "by hand". File is set to null.
   */
  public TarEntry(String name) {
    this.initialize();
    this.nameTarHeader(this.header, name);
  }

  /**
   * 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(File filethrows InvalidHeaderException {
    this.initialize();
    this.getFileTarHeader(this.header, file);
  }

  /**
   * Construct an entry from an archive's header bytes. File is set to null.
   
   @param headerBuf
   *          The header bytes from a tar archive entry.
   */
  public TarEntry(byte[] headerBufthrows InvalidHeaderException {
    this.initialize();
    this.parseTarHeader(this.header, headerBuf);
  }

  /**
   * Initialization code common to all constructors.
   */
  private void initialize() {
    this.file = null;
    this.header = new TarHeader();
  }

  /**
   * Determine if the two entries are equal. Equality is determined by the
   * header names being equal.
   
   @return it Entry to be checked for equality.
   @return True if the entries are equal.
   */
  public boolean equals(TarEntry it) {
    return this.header.name.toString().equals(it.header.name.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 this.
   @return True if entry is a descendant of this.
   */
  public boolean isDescendent(TarEntry desc) {
    return desc.header.name.toString().startsWith(this.header.name.toString());
  }

  /**
   * Get this entry's header.
   
   @return This entry's TarHeader.
   */
  public TarHeader getHeader() {
    return this.header;
  }

  /**
   * Get this entry's name.
   
   @return This entry's name.
   */
  public String getName() {
    return this.header.name.toString();
  }

  /**
   * Set this entry's name.
   
   @param name
   *          This entry's new name.
   */
  public void setName(String name) {
    this.header.name = new StringBuffer(name);
  }

  /**
   * Get this entry's user id.
   
   @return This entry's user id.
   */
  public int getUserId() {
    return this.header.userId;
  }

  /**
   * Set this entry's user id.
   
   @param userId
   *          This entry's new user id.
   */
  public void setUserId(int userId) {
    this.header.userId = userId;
  }

  /**
   * Get this entry's group id.
   
   @return This entry's group id.
   */
  public int getGroupId() {
    return this.header.groupId;
  }

  /**
   * Set this entry's group id.
   
   @param groupId
   *          This entry's new group id.
   */
  public void setGroupId(int groupId) {
    this.header.groupId = groupId;
  }

  /**
   * Get this entry's user name.
   
   @return This entry's user name.
   */
  public String getUserName() {
    return this.header.userName.toString();
  }

  /**
   * Set this entry's user name.
   
   @param userName
   *          This entry's new user name.
   */
  public void setUserName(String userName) {
    this.header.userName = new StringBuffer(userName);
  }

  /**
   * Get this entry's group name.
   
   @return This entry's group name.
   */
  public String getGroupName() {
    return this.header.groupName.toString();
  }

  /**
   * Set this entry's group name.
   
   @param groupName
   *          This entry's new group name.
   */
  public void setGroupName(String groupName) {
    this.header.groupName = new StringBuffer(groupName);
  }

  /**
   * Convenience method to set this entry's group and user ids.
   
   @param userId
   *          This entry's new user id.
   @param groupId
   *          This entry's new group id.
   */
  public void setIds(int userId, int groupId) {
    this.setUserId(userId);
    this.setGroupId(groupId);
  }

  /**
   * Convenience method to set this entry's group and user names.
   
   @param userName
   *          This entry's new user name.
   @param groupName
   *          This entry's new group name.
   */
  public void setNames(String userName, String groupName) {
    this.setUserName(userName);
    this.setGroupName(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(long time) {
    this.header.modTime = time / 1000;
  }

  /**
   * Set this entry's modification time.
   
   @param time
   *          This entry's new modification time.
   */
  public void setModTime(Date time) {
    this.header.modTime = time.getTime() 1000;
  }

  /**
   * Set this entry's modification time.
   
   @param time
   *          This entry's new modification time.
   */
  public Date getModTime() {
    return new Date(this.header.modTime * 1000);
  }

  /**
   * Get this entry's file.
   
   @return This entry's file.
   */
  public File getFile() {
    return this.file;
  }

  /**
   * Get this entry's file size.
   
   @return This entry's file size.
   */
  public long getSize() {
    return this.header.size;
  }

  /**
   * Set this entry's file size.
   
   @param size
   *          This entry's new file size.
   */
  public void setSize(long size) {
    this.header.size = size;
  }

  /**
   * Convenience method that will modify an entry's name directly in place in an
   * entry header buffer byte array.
   
   @param outbuf
   *          The buffer containing the entry header to modify.
   @param newName
   *          The new name to place into the header buffer.
   */
  public void adjustEntryName(byte[] outbuf, String newName) {
    int offset = 0;
    offset = TarHeader.getNameBytes(new StringBuffer(newName), outbuf, offset, TarHeader.NAMELEN);
  }

  /**
   * Return whether or not this entry represents a directory.
   
   @return True if this entry is a directory.
   */
  public boolean isDirectory() {
    if (this.file != null)
      return this.file.isDirectory();

    if (this.header != null) {
      if (this.header.linkFlag == TarHeader.LF_DIR)
        return true;

      if (this.header.name.toString().endsWith("/"))
        return true;
    }

    return false;
  }

  /**
   * Fill in a TarHeader with information from a File.
   
   @param hdr
   *          The TarHeader to fill in.
   @param file
   *          The file from which to get the header information.
   */
  public void getFileTarHeader(TarHeader hdr, File filethrows InvalidHeaderException {
    this.file = file;

    String name = file.getPath();
    String osname = System.getProperty("os.name");
    if (osname != null) {
      // Strip off drive letters!
      // REVIEW Would a better check be "(File.separator == '\')"?

      // String Win32Prefix = "Windows";
      // String prefix = osname.substring( 0, Win32Prefix.length() );
      // if ( prefix.equalsIgnoreCase( Win32Prefix ) )

      // if ( File.separatorChar == '\\' )

      // Per Patrick Beard:
      String Win32Prefix = "windows";
      if (osname.toLowerCase().startsWith(Win32Prefix)) {
        if (name.length() 2) {
          char ch1 = name.charAt(0);
          char ch2 = name.charAt(1);
          if (ch2 == ':' && ((ch1 >= 'a' && ch1 <= 'z'|| (ch1 >= 'A' && ch1 <= 'Z'))) {
            name = name.substring(2);
          }
        }
      }
    }

    name = name.replace(File.separatorChar, '/');

    // No absolute pathnames
    // Windows (and Posix?) paths can start with "\\NetworkDrive\",
    // so we loop on starting /'s.

    for (; name.startsWith("/");)
      name = name.substring(1);

    hdr.linkName = new StringBuffer("");

    hdr.name = new StringBuffer(name);

    if (file.isDirectory()) {
      hdr.mode = 040755;
      hdr.linkFlag = TarHeader.LF_DIR;
      if (hdr.name.charAt(hdr.name.length() 1!= '/')
        hdr.name.append("/");
    else {
      hdr.mode = 0100644;
      hdr.linkFlag = TarHeader.LF_NORMAL;
    }

    // UNDONE When File lets us get the userName, use it!

    hdr.size = file.length();
    hdr.modTime = file.lastModified() 1000;
    hdr.checkSum = 0;
    hdr.devMajor = 0;
    hdr.devMinor = 0;
  }

  /**
   * 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() throws InvalidHeaderException {
    if (this.file == null || !this.file.isDirectory()) {
      return new TarEntry[0];
    }

    String[] list = this.file.list();

    TarEntry[] result = new TarEntry[list.length];

    for (int i = 0; i < list.length; ++i) {
      result[inew TarEntry(new File(this.file, list[i]));
    }

    return result;
  }

  /**
   * Compute the checksum of a tar entry header.
   
   @param buf
   *          The tar entry's header buffer.
   @return The computed checksum.
   */
  public long computeCheckSum(byte[] buf) {
    long sum = 0;

    for (int i = 0; i < buf.length; ++i) {
      sum += 255 & buf[i];
    }

    return sum;
  }

  /**
   * Write an entry's header information to a header buffer.
   
   @param outbuf
   *          The tar entry header buffer to fill in.
   */
  public void writeEntryHeader(byte[] outbuf) {
    int offset = 0;

    offset = TarHeader.getNameBytes(this.header.name, outbuf, offset, TarHeader.NAMELEN);

    offset = TarHeader.getOctalBytes(this.header.mode, outbuf, offset, TarHeader.MODELEN);

    offset = TarHeader.getOctalBytes(this.header.userId, outbuf, offset, TarHeader.UIDLEN);

    offset = TarHeader.getOctalBytes(this.header.groupId, outbuf, offset, TarHeader.GIDLEN);

    long size = this.header.size;

    offset = TarHeader.getLongOctalBytes(size, outbuf, offset, TarHeader.SIZELEN);

    offset = TarHeader.getLongOctalBytes(this.header.modTime, outbuf, offset, TarHeader.MODTIMELEN);

    int csOffset = offset;
    for (int c = 0; c < TarHeader.CHKSUMLEN; ++c)
      outbuf[offset++(byte' ';

    outbuf[offset++this.header.linkFlag;

    offset = TarHeader.getNameBytes(this.header.linkName, outbuf, offset, TarHeader.NAMELEN);

    offset = TarHeader.getNameBytes(this.header.magic, outbuf, offset, TarHeader.MAGICLEN);

    offset = TarHeader.getNameBytes(this.header.userName, outbuf, offset, TarHeader.UNAMELEN);

    offset = TarHeader.getNameBytes(this.header.groupName, outbuf, offset, TarHeader.GNAMELEN);

    offset = TarHeader.getOctalBytes(this.header.devMajor, outbuf, offset, TarHeader.DEVLEN);

    offset = TarHeader.getOctalBytes(this.header.devMinor, outbuf, offset, TarHeader.DEVLEN);

    for (; offset < outbuf.length;)
      outbuf[offset++0;

    long checkSum = this.computeCheckSum(outbuf);

    TarHeader.getCheckSumOctalBytes(checkSum, outbuf, csOffset, TarHeader.CHKSUMLEN);
  }

  /**
   * Parse an entry's TarHeader information from a header buffer.
   
   @param hdr
   *          The TarHeader to fill in from the buffer information.
   @param header
   *          The tar entry header buffer to get information from.
   */
  public void parseTarHeader(TarHeader hdr, byte[] headerthrows InvalidHeaderException {
    int offset = 0;

    hdr.name = TarHeader.parseName(header, offset, TarHeader.NAMELEN);

    offset += TarHeader.NAMELEN;

    hdr.mode = (intTarHeader.parseOctal(header, offset, TarHeader.MODELEN);

    offset += TarHeader.MODELEN;

    hdr.userId = (intTarHeader.parseOctal(header, offset, TarHeader.UIDLEN);

    offset += TarHeader.UIDLEN;

    hdr.groupId = (intTarHeader.parseOctal(header, offset, TarHeader.GIDLEN);

    offset += TarHeader.GIDLEN;

    hdr.size = TarHeader.parseOctal(header, offset, TarHeader.SIZELEN);

    offset += TarHeader.SIZELEN;

    hdr.modTime = TarHeader.parseOctal(header, offset, TarHeader.MODTIMELEN);

    offset += TarHeader.MODTIMELEN;

    hdr.checkSum = (intTarHeader.parseOctal(header, offset, TarHeader.CHKSUMLEN);

    offset += TarHeader.CHKSUMLEN;

    hdr.linkFlag = header[offset++];

    hdr.linkName = TarHeader.parseName(header, offset, TarHeader.NAMELEN);

    offset += TarHeader.NAMELEN;

    hdr.magic = TarHeader.parseName(header, offset, TarHeader.MAGICLEN);

    offset += TarHeader.MAGICLEN;

    hdr.userName = TarHeader.parseName(header, offset, TarHeader.UNAMELEN);

    offset += TarHeader.UNAMELEN;

    hdr.groupName = TarHeader.parseName(header, offset, TarHeader.GNAMELEN);

    offset += TarHeader.GNAMELEN;

    hdr.devMajor = (intTarHeader.parseOctal(header, offset, TarHeader.DEVLEN);

    offset += TarHeader.DEVLEN;

    hdr.devMinor = (intTarHeader.parseOctal(header, offset, TarHeader.DEVLEN);
  }

  /**
   * Fill in a TarHeader given only the entry's name.
   
   @param hdr
   *          The TarHeader to fill in.
   @param name
   *          The tar entry name.
   */
  public void nameTarHeader(TarHeader hdr, String name) {
    boolean isDir = name.endsWith("/");

    hdr.checkSum = 0;
    hdr.devMajor = 0;
    hdr.devMinor = 0;

    hdr.name = new StringBuffer(name);
    hdr.mode = isDir ? 040755 0100644;
    hdr.userId = 0;
    hdr.groupId = 0;
    hdr.size = 0;
    hdr.checkSum = 0;

    hdr.modTime = (new java.util.Date()).getTime() 1000;

    hdr.linkFlag = isDir ? TarHeader.LF_DIR : TarHeader.LF_NORMAL;

    hdr.linkName = new StringBuffer("");
    hdr.userName = new StringBuffer("");
    hdr.groupName = new StringBuffer("");

    hdr.devMajor = 0;
    hdr.devMinor = 0;
  }

}

/**
 * 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.
 
 @version $Revision: 12504 $
 @author Timothy Gerard Endres, <a
 *         href="mailto:time@gjt.org">time@trustice.com</a>.
 @see TarArchive
 */
class TarBuffer extends Object {
  public static final int DEFAULT_RCDSIZE = (512);

  public static final int DEFAULT_BLKSIZE = (DEFAULT_RCDSIZE * 20);

  private InputStream inStream;

  private OutputStream outStream;

  private byte[] blockBuffer;

  private int currBlkIdx;

  private int currRecIdx;

  private int blockSize;

  private int recordSize;

  private int recsPerBlock;

  private boolean debug;

  public TarBuffer(InputStream inStream) {
    this(inStream, TarBuffer.DEFAULT_BLKSIZE);
  }

  public TarBuffer(InputStream inStream, int blockSize) {
    this(inStream, blockSize, TarBuffer.DEFAULT_RCDSIZE);
  }

  public TarBuffer(InputStream inStream, int blockSize, int recordSize) {
    this.inStream = inStream;
    this.outStream = null;
    this.initialize(blockSize, recordSize);
  }

  public TarBuffer(OutputStream outStream) {
    this(outStream, TarBuffer.DEFAULT_BLKSIZE);
  }

  public TarBuffer(OutputStream outStream, int blockSize) {
    this(outStream, blockSize, TarBuffer.DEFAULT_RCDSIZE);
  }

  public TarBuffer(OutputStream outStream, int blockSize, int recordSize) {
    this.inStream = null;
    this.outStream = outStream;
    this.initialize(blockSize, recordSize);
  }

  /**
   * Initialization common to all constructors.
   */
  private void initialize(int blockSize, int recordSize) {
    this.debug = false;
    this.blockSize = blockSize;
    this.recordSize = recordSize;
    this.recsPerBlock = (this.blockSize / this.recordSize);
    this.blockBuffer = new byte[this.blockSize];

    if (this.inStream != null) {
      this.currBlkIdx = -1;
      this.currRecIdx = this.recsPerBlock;
    else {
      this.currBlkIdx = 0;
      this.currRecIdx = 0;
    }
  }

  /**
   * Get the TAR Buffer's block size. Blocks consist of multiple records.
   */
  public int getBlockSize() {
    return this.blockSize;
  }

  /**
   * Get the TAR Buffer's record size.
   */
  public int getRecordSize() {
    return this.recordSize;
  }

  /**
   * Set the debugging flag for the buffer.
   
   @param debug
   *          If true, print debugging output.
   */
  public void setDebug(boolean debug) {
    this.debug = debug;
  }

  /**
   * 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.
   */
  public boolean isEOFRecord(byte[] record) {
    for (int i = 0, sz = this.getRecordSize(); i < sz; ++i)
      if (record[i!= 0)
        return false;

    return true;
  }

  /**
   * Skip over a record on the input stream.
   */

  public void skipRecord() throws IOException {
    if (this.debug) {
      System.err
          .println("SkipRecord: recIdx = " this.currRecIdx + " blkIdx = " this.currBlkIdx);
    }

    if (this.inStream == null)
      throw new IOException("reading (via skip) from an output buffer");

    if (this.currRecIdx >= this.recsPerBlock) {
      if (!this.readBlock())
        return// UNDONE
    }

    this.currRecIdx++;
  }

  /**
   * Read a record from the input stream and return the data.
   
   @return The record data.
   */

  public byte[] readRecord() throws IOException {
    if (this.debug) {
      System.err
          .println("ReadRecord: recIdx = " this.currRecIdx + " blkIdx = " this.currBlkIdx);
    }

    if (this.inStream == null)
      throw new IOException("reading from an output buffer");

    if (this.currRecIdx >= this.recsPerBlock) {
      if (!this.readBlock())
        return null;
    }

    byte[] result = new byte[this.recordSize];

    System.arraycopy(this.blockBuffer, (this.currRecIdx * this.recordSize), result, 0,
        this.recordSize);

    this.currRecIdx++;

    return result;
  }

  /**
   @return false if End-Of-File, else true
   */

  private boolean readBlock() throws IOException {
    if (this.debug) {
      System.err.println("ReadBlock: blkIdx = " this.currBlkIdx);
    }

    if (this.inStream == null)
      throw new IOException("reading from an output buffer");

    this.currRecIdx = 0;

    int offset = 0;
    int bytesNeeded = this.blockSize;
    for (; bytesNeeded > 0;) {
      long numBytes = this.inStream.read(this.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)
        break;

      offset += numBytes;
      bytesNeeded -= numBytes;
      if (numBytes != this.blockSize) {
        if (this.debug) {
          System.err.println("ReadBlock: INCOMPLETE READ " + numBytes + " of " this.blockSize
              " bytes read.");
        }
      }
    }

    this.currBlkIdx++;

    return true;
  }

  /**
   * Get the current block number, zero based.
   
   @return The current zero based block number.
   */
  public int getCurrentBlockNum() {
    return this.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 this.currRecIdx - 1;
  }

  /**
   * Write an archive record to the archive.
   
   @param record
   *          The record data to write to the archive.
   */

  public void writeRecord(byte[] recordthrows IOException {
    if (this.debug) {
      System.err.println("WriteRecord: recIdx = " this.currRecIdx + " blkIdx = "
          this.currBlkIdx);
    }

    if (this.outStream == null)
      throw new IOException("writing to an input buffer");

    if (record.length != this.recordSize)
      throw new IOException("record to write has length '" + record.length
          "' which is not the record size of '" this.recordSize + "'");

    if (this.currRecIdx >= this.recsPerBlock) {
      this.writeBlock();
    }

    System.arraycopy(record, 0this.blockBuffer, (this.currRecIdx * this.recordSize),
        this.recordSize);

    this.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 buf
   *          The buffer containing the record data to write.
   @param offset
   *          The offset of the record data within buf.
   */

  public void writeRecord(byte[] buf, int offsetthrows IOException {
    if (this.debug) {
      System.err.println("WriteRecord: recIdx = " this.currRecIdx + " blkIdx = "
          this.currBlkIdx);
    }

    if (this.outStream == null)
      throw new IOException("writing to an input buffer");

    if ((offset + this.recordSize> buf.length)
      throw new IOException("record has length '" + buf.length + "' with offset '" + offset
          "' which is less than the record size of '" this.recordSize + "'");

    if (this.currRecIdx >= this.recsPerBlock) {
      this.writeBlock();
    }

    System.arraycopy(buf, offset, this.blockBuffer, (this.currRecIdx * this.recordSize),
        this.recordSize);

    this.currRecIdx++;
  }

  /**
   * Write a TarBuffer block to the archive.
   */
  private void writeBlock() throws IOException {
    if (this.debug) {
      System.err.println("WriteBlock: blkIdx = " this.currBlkIdx);
    }

    if (this.outStream == null)
      throw new IOException("writing to an input buffer");

    this.outStream.write(this.blockBuffer, 0this.blockSize);
    this.outStream.flush();

    this.currRecIdx = 0;
    this.currBlkIdx++;
  }

  /**
   * Flush the current data block if it has any data in it.
   */

  private void flushBlock() throws IOException {
    if (this.debug) {
      System.err.println("TarBuffer.flushBlock() called.");
    }

    if (this.outStream == null)
      throw new IOException("writing to an input buffer");

    if (this.currRecIdx > 0) {
      this.writeBlock();
    }
  }

  /**
   * Close the TarBuffer. If this is an output buffer, also flush the current
   * block before closing.
   */
  public void close() throws IOException {
    if (this.debug) {
      System.err.println("TarBuffer.closeBuffer().");
    }

    if (this.outStream != null) {
      this.flushBlock();

      if (this.outStream != System.out && this.outStream != System.err) {
        this.outStream.close();
        this.outStream = null;
      }
    else if (this.inStream != null) {
      if (this.inStream != System.in) {
        this.inStream.close();
        this.inStream = null;
      }
    }
  }

}

class InvalidHeaderException extends IOException {

  public InvalidHeaderException() {
    super();
  }

  public InvalidHeaderException(String msg) {
    super(msg);
  }

}

   
    
    
    
    
  
Related examples in the same category
1. Extract contents of a zip file
2. List the contents of a zip file
3. Read entries in a zip / compressed file
4. Decompress a zip file using ZipInputStream
5. Decompress a zip file using ZipFile
6. Create checksum for a zip file
7. Read a zip file checksum value
8. Create a zip file with java.util.zip package
9. Extract file/files from a zip file
10. Read files within a zip file
11. Retrieve a compressed file from a ZIP file
12. Retrieve the contents of a ZIP file
13. Making a zip file of directory including its subdirectories recursively
14. Displaying contents of a compressed zip file
15. Compress a Byte Array
16. Decompress a Byte Array
17. Read zip file
18. Write Zip file
19. The java.util.zip package can be used to create a checksum.
20. Read the content of a zip file ZipFile
21. List the entries of a zip file
22. Compressing Streams: Zipper, Java example
23. Compressing Streams: Parity Checksum
24. Compressing Streams: File Summer
25. Create a simple ZIP File: not retain any directory path information about the files.
26. Decompress a ZIP file.
27. Decompressing a Byte Array
28. Zip unzip byte array
29. Creating a ZIP File
30. Listing the Contents of a ZIP File
31. Retrieving a Compressed File from a ZIP File
32. Calculating the Checksum of a Byte Array (Compute Adler-32 checksum)
33. Compute CRC-32 checksum
34. Calculating the Checksum of a File
35. Compress string(byte array) by Deflater
36. Use Java code to zip a folder
37. Uses Zip compression to compress any number of files given on the command line
38. Load zip file and scan zip file
39. Reading the Contents of a ZIP File
40. UnZip -- print or unzip a JAR or PKZIP file using java.util.zip
41. Tape Archive Lister: Tar file
42. Compressing a Byte Array
43. Tar file stream
44. Tar file and untar file
45. bzip source code
46. Unpack an archive from a URL
47. Compare two zip files
48. Determine whether a file is a ZIP File.
49. Zip up a directory
50. Check sum for a path
51. Check sum for an InputStream
52. Extract zip file to destination folder
53. Return the first directory of this archive. This is needed to determine the plugin directory
54. Makes a zip file named xmlFileName from xmlURL at path
55. Unzipps a zip file placed at zipURL to path
56. A single checksum calculation for multiple files
57. Put file To Zip File
58. Provides both writing and reading from a file which is transparently compressed in Zip
59. TarInputStream reads a UNIX tar archive as an InputStream
60. Package files utility
61. Zip Compare
62. Unpack a segment from a zip
63. Unpack a zip file
64. Unzip file to a directory
65. Zip a list of file into one zip file.
66. Validate that an archive contains a named entry
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.