dotgnu-pnet-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Dotgnu-pnet-commits] pnetlib/SharpZipLib/Tar InvalidHeaderException.cs,


From: Rhys Weatherley <address@hidden>
Subject: [Dotgnu-pnet-commits] pnetlib/SharpZipLib/Tar InvalidHeaderException.cs, NONE, 1.1 TarArchive.cs, NONE, 1.1 TarBuffer.cs, NONE, 1.1 TarEntry.cs, NONE, 1.1 TarHeader.cs, NONE, 1.1 TarInputStream.cs, NONE, 1.1 TarOutputStream.cs, NONE, 1.1
Date: Wed, 08 Oct 2003 07:37:07 +0000

Update of /cvsroot/dotgnu-pnet/pnetlib/SharpZipLib/Tar
In directory subversions:/tmp/cvs-serv5756/SharpZipLib/Tar

Added Files:
        InvalidHeaderException.cs TarArchive.cs TarBuffer.cs 
        TarEntry.cs TarHeader.cs TarInputStream.cs TarOutputStream.cs 
Log Message:


Add the "SharpZipLib" directory to pnetlib, which is a port of
SharpDevelop's compression handling library.


--- NEW FILE: TarHeader.cs ---
// TarHeader.cs
// Copyright (C) 2001 Mike Krueger
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
// Linking this library statically or dynamically with other modules is
// making a combined work based on this library.  Thus, the terms and
// conditions of the GNU General Public License cover the whole
// combination.
// 
// As a special exception, the copyright holders of this library give you
// permission to link this library with independent modules to produce an
// executable, regardless of the license terms of these independent
// modules, and to copy and distribute the resulting executable under
// terms of your choice, provided that you also meet, for each linked
// independent module, the terms and conditions of the license of that
// module.  An independent module is a module which is not derived from
// or based on this library.  If you modify this library, you may extend
// this exception to your version of the library, but you are not
// obligated to do so.  If you do not wish to do so, delete this
// exception statement from your version.

using System;
using System.Text;

namespace ICSharpCode.SharpZipLib.Tar 
{
        
        
        /// <summary>
        /// This class encapsulates the Tar Entry Header used in Tar Archives.
        /// The class also holds a number of tar constants, used mostly in 
headers.
        /// </summary>
        public class TarHeader : ICloneable
        {
                /// <summary>
                /// The length of the name field in a header buffer.
                /// </summary>
                public readonly static int NAMELEN = 100;
                
                /// <summary>
                /// The length of the mode field in a header buffer.
                /// </summary>
                public readonly static int MODELEN = 8;
                
                /// <summary>
                /// The length of the user id field in a header buffer.
                /// </summary>
                public readonly static int UIDLEN = 8;
                
                /// <summary>
                /// The length of the group id field in a header buffer.
                /// </summary>
                public readonly static int GIDLEN = 8;
                
                /// <summary>
                /// The length of the checksum field in a header buffer.
                /// </summary>
                public readonly static int CHKSUMLEN = 8;
                
                /// <summary>
                /// The length of the size field in a header buffer.
                /// </summary>
                public readonly static int SIZELEN = 12;
                
                /// <summary>
                /// The length of the magic field in a header buffer.
                /// </summary>
                public readonly static int MAGICLEN = 8;
                
                /// <summary>
                /// The length of the modification time field in a header 
buffer.
                /// </summary>
                public readonly static int MODTIMELEN = 12;
                
                /// <summary>
                /// The length of the user name field in a header buffer.
                /// </summary>
                public readonly static int UNAMELEN = 32;
                
                /// <summary>
                /// The length of the group name field in a header buffer.
                /// </summary>
                public readonly static int GNAMELEN = 32;
                
                /// <summary>
                /// The length of the devices field in a header buffer.
                /// </summary>
                public readonly static int DEVLEN = 8;
                
                /// <summary>
                /// 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.
                /// </summary>
                public readonly static byte     LF_OLDNORM      = 0;
                
                /// <summary>
                /// Normal file type.
                /// </summary>
                public readonly static byte     LF_NORMAL       = (byte) '0';
                
                /// <summary>
                /// Link file type.
                /// </summary>
                public readonly static byte     LF_LINK         = (byte) '1';
                
                /// <summary>
                /// Symbolic link file type.
                /// </summary>
                public readonly static byte     LF_SYMLINK      = (byte) '2';
                
                /// <summary>
                /// Character device file type.
                /// </summary>
                public readonly static byte     LF_CHR          = (byte) '3';
                
                /// <summary>
                /// Block device file type.
                /// </summary>
                public readonly static byte     LF_BLK          = (byte) '4';
                
                /// <summary>
                /// Directory file type.
                /// </summary>
                public readonly static byte     LF_DIR          = (byte) '5';
                
                /// <summary>
                /// FIFO (pipe) file type.
                /// </summary>
                public readonly static byte     LF_FIFO         = (byte) '6';
                
                /// <summary>
                /// Contiguous file type.
                /// </summary>
                public readonly static byte     LF_CONTIG       = (byte) '7';
                
                /// <summary>
                /// The magic tag representing a POSIX tar archive.
                /// </summary>
                public readonly static string   TMAGIC          = "ustar";
                
                /// <summary>
                /// The magic tag representing a GNU tar archive.
                /// </summary>
                public readonly static string   GNU_TMAGIC      = "ustar  ";
                
                /// <summary>
                /// The entry's name.
                /// </summary>
                public StringBuilder name;
                
                /// <summary>
                /// The entry's permission mode.
                /// </summary>
                public int mode;
                
                /// <summary>
                /// The entry's user id.
                /// </summary>
                public int userId;
                
                /// <summary>
                /// The entry's group id.
                /// </summary>
                public int groupId;
                
                /// <summary>
                /// The entry's size.
                /// </summary>
                public long size;
                
                /// <summary>
                /// The entry's modification time.
                /// </summary>
                public DateTime modTime;
                
                /// <summary>
                /// The entry's checksum.
                /// </summary>
                public int checkSum;
                
                /// <summary>
                /// The entry's link flag.
                /// </summary>
                public byte linkFlag;
                
                /// <summary>
                /// The entry's link name.
                /// </summary>
                public StringBuilder linkName;
                
                /// <summary>
                /// The entry's magic tag.
                /// </summary>
                public StringBuilder magic;
                
                /// <summary>
                /// The entry's user name.
                /// </summary>
                public StringBuilder userName;
                
                /// <summary>
                /// The entry's group name.
                /// </summary>
                public StringBuilder groupName;
                
                /// <summary>
                /// The entry's major device number.
                /// </summary>
                public int devMajor;
                
                /// <summary>
                /// The entry's minor device number.
                /// </summary>
                public int devMinor;
                
                public TarHeader()
                {
                        this.magic = new StringBuilder(TarHeader.TMAGIC);
                        
                        this.name     = new StringBuilder();
                        this.linkName = new StringBuilder();
                        
                        //string user = Environment.UserName;
                        string user = "PocketPC";
                        
                        if (user.Length > 31) 
                        {
                                user = user.Substring(0, 31);
                        }
                        
                        this.userId    = 0;
                        this.groupId   = 0;
                        this.userName  = new StringBuilder(user);
                        this.groupName = new StringBuilder(String.Empty);
                }
                
                /// <summary>
                /// TarHeaders can be cloned.
                /// </summary>
                public object Clone()
                {
                        TarHeader hdr = new TarHeader();
                        
                        hdr.name      = (this.name == null) ? null : new 
StringBuilder(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 == null)  ? null : new 
StringBuilder(this.linkName.ToString());
                        hdr.magic     = (this.magic == null)     ? null : new 
StringBuilder(this.magic.ToString());
                        hdr.userName  = (this.userName == null)  ? null : new 
StringBuilder(this.userName.ToString());
                        hdr.groupName = (this.groupName == null) ? null : new 
StringBuilder(this.groupName.ToString());
                        hdr.devMajor  = this.devMajor;
                        hdr.devMinor  = this.devMinor;
                        
                        return hdr;
                }
                
                /// <summary>
                /// Get the name of this entry.
                /// </summary>
                /// <returns>
                /// The entry's name.
                /// </returns>
                public string GetName()
                {
                        return this.name.ToString();
                }
                
                /// <summary>
                /// Parse an octal string from a header buffer. This is used 
for the
                /// file permission mode value.
                /// </summary>
                /// <param name = "header">
                /// The header buffer from which to parse.
                /// </param>
                /// <param name = "offset">
                /// The offset into the buffer from which to parse.
                /// </param>
                /// <param name = "length">
                /// The number of header bytes to parse.
                /// </param>
                /// <returns>
                /// The long value of the octal string.
                /// </returns>
                public static long ParseOctal(byte[] header, int offset, int 
length)
                {
                        long result = 0;
                        bool 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;
                }
                
                /// <summary>
                /// Parse an entry name from a header buffer.
                /// </summary>
                /// <param name="header">
                /// The header buffer from which to parse.
                /// </param>
                /// <param name="offset">
                /// The offset into the buffer from which to parse.
                /// </param>
                /// <param name="length">
                /// The number of header bytes to parse.
                /// </param>
                /// <returns>
                /// The header's entry name.
                /// </returns>
                public static StringBuilder ParseName(byte[] header, int 
offset, int length)
                {
                        StringBuilder result = new StringBuilder(length);
                        
                        for (int i = offset; i < offset + length; ++i) 
                        {
                                if (header[i] == 0) 
                                {
                                        break;
                                }
                                result.Append((char)header[i]);
                        }
                        
                        return result;
                }
                
                /// <summary>
                /// Determine the number of bytes in an entry name.
                /// </summary>
                /// <param name="name">
                /// </param>
                /// <param name="buf">
                /// The header buffer from which to parse.
                /// </param>
                /// <param name="offset">
                /// The offset into the buffer from which to parse.
                /// </param>
                /// <param name="length">
                /// The number of header bytes to parse.
                /// </param>
                /// <returns>
                /// The number of bytes in a header's entry name.
                /// </returns>
                public static int GetNameBytes(StringBuilder name, byte[] buf, 
int offset, int length)
                {
                        int i;
                        
                        for (i = 0 ; i < length && i < name.Length; ++i) 
                        {
                                buf[offset + i] = (byte)name[i];
                        }
                        
                        for (; i < length ; ++i) 
                        {
                                buf[offset + i] = 0;
                        }
                        
                        return offset + length;
                }
                
                /// <summary>
                /// Parse an octal integer from a header buffer.
                /// </summary>
                /// <param name = "val">
                /// </param>
                /// <param name = "buf">
                /// The header buffer from which to parse.
                /// </param>
                /// <param name = "offset">
                /// The offset into the buffer from which to parse.
                /// </param>
                /// <param name = "length">
                /// The number of header bytes to parse.
                /// </param>
                /// <returns>
                /// The integer value of the octal bytes.
                /// </returns>
                public static int GetOctalBytes(long val, byte[] buf, int 
offset, int length)
                {
                        byte[] result = new byte[length];
                        
                        int idx = length - 1;
                        
                        buf[offset + idx] = 0;
                        --idx;
                        buf[offset + idx] = (byte)' ';
                        --idx;
                        
                        if (val == 0) 
                        {
                                buf[offset + idx] = (byte)'0';
                                --idx;
                        } 
                        else 
                        {
                                for (long v = val; idx >= 0 && v > 0; --idx) 
                                {
                                        buf[offset + idx] = (byte)((byte)'0' + 
(byte)(v & 7));
                                        v >>= 3;
                                }
                        }
                                
                        for (; idx >= 0; --idx) 
                        {
                                buf[offset + idx] = (byte)' ';
                        }
                        
                        return offset + length;
                }
                
                /// <summary>
                /// Parse an octal long integer from a header buffer.
                /// </summary>
                /// <param name = "val">
                /// </param>
                /// <param name = "buf">
                /// The header buffer from which to parse.
                /// </param>
                /// <param name = "offset">
                /// The offset into the buffer from which to parse.
                /// </param>
                /// <param name = "length">
                /// The number of header bytes to parse.
                /// </param>
                /// <returns>
                /// The long value of the octal bytes.
                /// </returns>
                public static int GetLongOctalBytes(long val, byte[] buf, int 
offset, int length)
                {
                        byte[] temp = new byte[length + 1];
                        TarHeader.GetOctalBytes(val, temp, 0, length + 1);
                        Array.Copy(temp, 0, buf, offset, length);
                        return offset + length;
                }
                
                /// <summary>
                /// Parse the checksum octal integer from a header buffer.
                /// </summary>
                /// <param name = "val">
                /// </param>
                /// <param name = "buf">
                /// The header buffer from which to parse.
                /// </param>
                /// <param name = "offset">
                /// The offset into the buffer from which to parse.
                /// </param>
                /// <param name = "length">
                /// The number of header bytes to parse.
                /// </param>
                /// <returns>
                /// The integer value of the entry's checksum.
                /// </returns>
                public static int GetCheckSumOctalBytes(long val, byte[] buf, 
int offset, int length)
                {
                        TarHeader.GetOctalBytes(val, buf, offset, length);
                        buf[offset + length - 1] = (byte)' ';
                        buf[offset + length - 2] = 0;
                        return offset + length;
                }
        }
}

/* The original Java file had this header:
 * 
** Authored by Timothy Gerard Endres
** <mailto:address@hidden>  <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. 
** 
*/

--- NEW FILE: TarInputStream.cs ---
// TarInputStream.cs
// Copyright (C) 2001 Mike Krueger
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
// Linking this library statically or dynamically with other modules is
// making a combined work based on this library.  Thus, the terms and
// conditions of the GNU General Public License cover the whole
// combination.
//
// As a special exception, the copyright holders of this library give you
// permission to link this library with independent modules to produce an
// executable, regardless of the license terms of these independent
// modules, and to copy and distribute the resulting executable under
// terms of your choice, provided that you also meet, for each linked
// independent module, the terms and conditions of the license of that
// module.  An independent module is a module which is not derived from
// or based on this library.  If you modify this library, you may extend
// this exception to your version of the library, but you are not
// obligated to do so.  If you do not wish to do so, delete this
// exception statement from your version.

using System;
using System.IO;
using System.Text;

namespace ICSharpCode.SharpZipLib.Tar 
{
        
        /// <summary>
        /// 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().
        /// </summary>
        public class TarInputStream : Stream
        {
                protected bool debug;
                protected bool hasHitEOF;
                
                protected int entrySize;
                protected int entryOffset;
                
                protected byte[] readBuf;
                
                protected TarBuffer buffer;
                protected TarEntry  currEntry;
                protected IEntryFactory eFactory;
                
                Stream inputStream;
                /// <summary>
                /// I needed to implement the abstract member.
                /// </summary>
                public override bool CanRead 
                {
                        get 
                        {
                                return inputStream.CanRead;
                        }
                }
                
                /// <summary>
                /// I needed to implement the abstract member.
                /// </summary>
                public override bool CanSeek 
                {
                        get 
                        {
                                return inputStream.CanSeek;
                        }
                }
                
                /// <summary>
                /// I needed to implement the abstract member.
                /// </summary>
                public override bool CanWrite 
                {
                        get 
                        {
                                return inputStream.CanWrite;
                        }
                }
                
                /// <summary>
                /// I needed to implement the abstract member.
                /// </summary>
                public override long Length 
                {
                        get 
                        {
                                return inputStream.Length;
                        }
                }
                
                /// <summary>
                /// I needed to implement the abstract member.
                /// </summary>
                public override long Position 
                {
                        get 
                        {
                                return inputStream.Position;
                        }
                        set 
                        {
                                inputStream.Position = value;
                        }
                }
                
                /// <summary>
                /// Flushes the baseInputStream
                /// </summary>
                public override void Flush()
                {
                        inputStream.Flush();
                }
                
                /// <summary>
                /// I needed to implement the abstract member.
                /// </summary>
                public override long Seek(long offset, SeekOrigin origin)
                {
                        return inputStream.Seek(offset, origin);
                }
                
                /// <summary>
                /// I needed to implement the abstract member.
                /// </summary>
                public override void SetLength(long val)
                {
                        inputStream.SetLength(val);
                }
                
                /// <summary>
                /// I needed to implement the abstract member.
                /// </summary>
                public override void Write(byte[] array, int offset, int count)
                {
                        inputStream.Write(array, offset, count);
                }
                
                /// <summary>
                /// I needed to implement the abstract member.
                /// </summary>
                public override void WriteByte(byte val)
                {
                        inputStream.WriteByte(val);
                }
                        
                
                public TarInputStream(Stream inputStream) : this(inputStream, 
TarBuffer.DEFAULT_BLKSIZE, TarBuffer.DEFAULT_RCDSIZE)
                {
                }
                
                public TarInputStream(Stream inputStream, int blockSize) : 
this(inputStream, blockSize, TarBuffer.DEFAULT_RCDSIZE)
                {
                }
                
                public TarInputStream(Stream inputStream, int blockSize, int 
recordSize)
                {
                        this.inputStream = inputStream;
                        this.buffer      = 
TarBuffer.CreateInputTarBuffer(inputStream, blockSize, recordSize);
                        
                        this.readBuf = null;
                        this.debug     = false;
                        this.hasHitEOF = false;
                        this.eFactory  = null;
                }
                
                public void SetDebug(bool debugF)
                {
                        this.debug = debugF;
                        SetBufferDebug(debugF);
                }
                public void SetBufferDebug(bool debug)
                {
                        this.buffer.SetDebug(debug);
                }
                
                
                
                public void SetEntryFactory(IEntryFactory factory)
                {
                        this.eFactory = factory;
                }
                
                /// <summary>
                /// Closes this stream. Calls the TarBuffer's close() method.
                /// The underlying stream is closed by the TarBuffer.
                /// </summary>
                public override void Close()
                {
                        this.buffer.Close();
                }
                
                /// <summary>
                /// Get the record size being used by this stream's TarBuffer.
                /// </summary>
                /// <returns>
                /// TarBuffer record size.
                /// </returns>
                public int GetRecordSize()
                {
                        return this.buffer.GetRecordSize();
                }
                
                /// <summary>
                /// 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.
                /// </summary>
                /// <returns>
                /// The number of available bytes for the current entry.
                /// </returns>
                public int Available 
                {
                        get 
                        {
                                return this.entrySize - this.entryOffset;
                        }
                }
                
                /// <summary>
                /// 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.
                /// </summary>
                /// <param name="numToSkip">
                /// The number of bytes to skip.
                /// </param>
                public void Skip(int numToSkip)
                {
                        // REVIEW
                        // This is horribly inefficient, but it ensures that we
                        // properly skip over bytes via the TarBuffer...
                        //
                        byte[] skipBuf = new byte[8 * 1024];
                        
                        for (int num = numToSkip; num > 0;)
                        {
                                int numRead = this.Read(skipBuf, 0, (num > 
skipBuf.Length ? skipBuf.Length : num));
                                
                                if (numRead == -1) 
                                {
                                        break;
                                }
                                
                                num -= numRead;
                        }
                }
                
                /// <summary>
                /// Since we do not support marking just yet, we return false.
                /// </summary>
                public bool IsMarkSupported 
                {
                        get 
                        {
                                return false;
                        }
                }
                
                /// <summary>
                /// Since we do not support marking just yet, we do nothing.
                /// </summary>
                /// <param name ="markLimit">
                /// The limit to mark.
                /// </param>
                public void Mark(int markLimit)
                {
                }
                
                /// <summary>
                /// Since we do not support marking just yet, we do nothing.
                /// </summary>
                public void Reset()
                {
                }
                
                /// <summary>
                /// 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.
                /// </summary>
                /// <returns>
                /// The next TarEntry in the archive, or null.
                /// </returns>
                public TarEntry GetNextEntry()
                {
                        if (this.hasHitEOF) 
                        {
                                return null;
                        }
                        
                        if (this.currEntry != null) 
                        {
                                int numToSkip = this.entrySize - 
this.entryOffset;
                                
                                if (this.debug) 
                                {
                                        
//Console.WriteLine.WriteLine("TarInputStream: SKIP currENTRY '" + 
this.currEntry.Name + "' SZ " + this.entrySize + " OFF " + this.entryOffset + " 
 skipping " + numToSkip + " bytes");
                                }
                                
                                if (numToSkip > 0) 
                                {
                                        this.Skip(numToSkip);
                                }
                                
                                this.readBuf = null;
                        }
                        
                        byte[] headerBuf = this.buffer.ReadRecord();
                        
                        if (headerBuf == null) 
                        {
                                if (this.debug) 
                                {
                                        //Console.WriteLine.WriteLine("READ 
NULL RECORD");
                                }
                                
                                this.hasHitEOF = true;
                        } 
                        else if (this.buffer.IsEOFRecord(headerBuf)) 
                        {
                                if (this.debug) 
                                {
                                        //Console.WriteLine.WriteLine( "READ 
EOF RECORD" );
                                }
                                
                                this.hasHitEOF = true;
                        }
                        
                        if (this.hasHitEOF) 
                        {
                                this.currEntry = null;
                        } 
                        else 
                        {
                                try 
                                {
                                        if (this.eFactory == null) 
                                        {
                                                this.currEntry = new 
TarEntry(headerBuf);
                                        } 
                                        else 
                                        {
                                                this.currEntry = 
this.eFactory.CreateEntry(headerBuf);
                                        }
                                        
                                        if (!(headerBuf[257] == 'u' && 
headerBuf[258] == 's' && headerBuf[259] == 't' && headerBuf[260] == 'a' && 
headerBuf[261] == 'r')) 
                                        {
                                                throw new 
InvalidHeaderException("header magic is not 'ustar', but '" + headerBuf[257] + 
headerBuf[258] + headerBuf[259] + headerBuf[260] + headerBuf[261] + 
                                                        "', or (dec) " + 
((int)headerBuf[257]) + ", " + ((int)headerBuf[258]) + ", " + 
((int)headerBuf[259]) + ", " + ((int)headerBuf[260]) + ", " + 
((int)headerBuf[261]));
                                        }
                                                
                                        if (this.debug) 
                                        {
                                                
//Console.WriteLine.WriteLine("TarInputStream: SET CURRENTRY '" + 
this.currEntry.Name + "' size = " + this.currEntry.Size);
                                        }
                        
                                        this.entryOffset = 0;
                                        
                                        // REVIEW How do we resolve this 
discrepancy?!
                                        this.entrySize = (int) 
this.currEntry.Size;
                                } 
                                catch (InvalidHeaderException ex) 
                                {
                                        this.entrySize = 0;
                                        this.entryOffset = 0;
                                        this.currEntry = null;
                                        throw new InvalidHeaderException("bad 
header in block " + this.buffer.GetCurrentBlockNum() + " record " + 
this.buffer.GetCurrentRecordNum() + ", " + ex.Message);
                                }
                        }
                        return this.currEntry;
                }
                
                /// <summary>
                /// Reads a byte from the current tar archive entry.
                /// This method simply calls read(byte[], int, int).
                /// </summary>
                public override int ReadByte()
                {
                        byte[] oneByteBuffer = new byte[1];
                        int num = this.Read(oneByteBuffer, 0, 1);
                        if (num <= 0) 
                        { // return -1 to indicate that no byte was read.
                                return -1;
                        }
                        return (int)oneByteBuffer[0];
                }
                
                /// <summary>
                /// 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
                /// entry in the archive and will deal with them as if they
                /// </summary>
                /// <param name="buf">
                /// The buffer into which to place bytes read.
                /// </param>
                /// <param name="offset">
                /// The offset at which to place bytes read.
                /// </param>
                /// <param name="numToRead">
                /// The number of bytes to read.
                /// </param>
                /// <returns>
                /// The number of bytes read, or -1 at EOF.
                /// </returns>
                public override int Read(byte[] buf, int offset, int numToRead)
                {
                        int totalRead = 0;
                        
                        if (this.entryOffset >= this.entrySize) 
                        {
                                return -1;
                        }
                        
                        if ((numToRead + this.entryOffset) > this.entrySize) 
                        {
                                numToRead = this.entrySize - this.entryOffset;
                        }
                        
                        if (this.readBuf != null) 
                        {
                                int sz = (numToRead > this.readBuf.Length) ? 
this.readBuf.Length : numToRead;
                                
                                Array.Copy(this.readBuf, 0, buf, offset, sz);
                                
                                if (sz >= this.readBuf.Length) 
                                {
                                        this.readBuf = null;
                                } 
                                else 
                                {
                                        int newLen = this.readBuf.Length - sz;
                                        byte[] newBuf = new byte[ newLen ];
                                        Array.Copy(this.readBuf, sz, newBuf, 0, 
newLen);
                                        this.readBuf = newBuf;
                                }
                                
                                totalRead += sz;
                                numToRead -= sz;
                                offset += sz;
                        }
                        
                        while (numToRead > 0) 
                        {
                                byte[] rec = this.buffer.ReadRecord();
                                if (rec == null) 
                                {
                                        // Unexpected EOF!
                                        throw new IOException("unexpected EOF 
with " + numToRead + " bytes unread");
                                }
                                
                                int sz     = numToRead;
                                int recLen = rec.Length;
                                
                                if (recLen > sz) 
                                {
                                        Array.Copy(rec, 0, buf, offset, sz);
                                        this.readBuf = new byte[recLen - sz];
                                        Array.Copy(rec, sz, this.readBuf, 0, 
recLen - sz);
                                } 
                                else 
                                {
                                        sz = recLen;
                                        Array.Copy(rec, 0, buf, offset, recLen);
                                }
                                
                                totalRead += sz;
                                numToRead -= sz;
                                offset += sz;
                        }
                        
                        this.entryOffset += totalRead;
                        
                        return totalRead;
                }
                
                /// <summary>
                /// Copies the contents of the current tar archive entry 
directly into
                /// an output stream.
                /// </summary>
                /// <param name="outputStream">
                /// The OutputStream into which to write the entry's data.
                /// </param>
                public void CopyEntryContents(Stream outputStream)
                {
                        byte[] buf = new byte[32 * 1024];
                        
                        while (true) 
                        {
                                int numRead = this.Read(buf, 0, buf.Length);
                                if (numRead <= 0) 
                                {
                                        break;
                                }
                                outputStream.Write(buf, 0, numRead);
                        }
                }
                
                /// <summary>
                /// This interface is provided, with the method 
setEntryFactory(), to allow
                /// the programmer to have their own TarEntry subclass 
instantiated for the
                /// entries return from getNextEntry().
                /// </summary>
                public interface IEntryFactory
                {
                        TarEntry CreateEntry(string name);
                        
                        TarEntry CreateEntryFromFile(string fileName);
                        
                        TarEntry CreateEntry(byte[] headerBuf);
                }
                
                public class EntryFactoryAdapter : IEntryFactory
                {
                        public TarEntry CreateEntry(string name)
                        {
                                return TarEntry.CreateTarEntry(name);
                        }
                        
                        public TarEntry CreateEntryFromFile(string fileName)
                        {
                                return TarEntry.CreateEntryFromFile(fileName);
                        }
                        
                        public TarEntry CreateEntry(byte[] headerBuf)
                        {
                                return new TarEntry(headerBuf);
                        }
                }
        }
        
        
}

/* The original Java file had this header:
        ** Authored by Timothy Gerard Endres
        ** <mailto:address@hidden>  <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.
        **
        */
        

--- NEW FILE: TarEntry.cs ---
// TarEntry.cs
// Copyright (C) 2001 Mike Krueger
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
// Linking this library statically or dynamically with other modules is
// making a combined work based on this library.  Thus, the terms and
// conditions of the GNU General Public License cover the whole
// combination.
// 
// As a special exception, the copyright holders of this library give you
// permission to link this library with independent modules to produce an
// executable, regardless of the license terms of these independent
// modules, and to copy and distribute the resulting executable under
// terms of your choice, provided that you also meet, for each linked
// independent module, the terms and conditions of the license of that
// module.  An independent module is a module which is not derived from
// or based on this library.  If you modify this library, you may extend
// this exception to your version of the library, but you are not
// obligated to do so.  If you do not wish to do so, delete this
// exception statement from your version.

using System;
using System.IO;
using System.Text;

namespace ICSharpCode.SharpZipLib.Tar 
{
        
        /// <summary>
        /// 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>
        /// <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>
        /// <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>
        /// 
        /// <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>
        /// </p>
        /// <see cref="TarHeader"/>
        /// </summary>
        public class TarEntry
        {
                /// <summary>
                /// If this entry represents a File, this references it.
                /// </summary>
                protected string    file;
                
                /// <summary>
                /// This is the entry's header information.
                /// </summary>
                protected TarHeader     header;
                
                /// <summary>
                /// Only Create Entries with the static CreateXYZ methods or a 
headerBuffer.
                /// </summary>
                private TarEntry()
                {
                }
                
                /// <summary>
                /// Construct an entry from an archive's header bytes. File is 
set
                /// to null.
                /// </summary>
                /// <param name = "headerBuf">
                /// The header bytes from a tar archive entry.
                /// </param>
                public TarEntry(byte[] headerBuf)
                {
                        this.Initialize();
                        this.ParseTarHeader(this.header, headerBuf);
                }
                
                                
                
                /// <summary>
                /// Construct an entry with only a name. This allows the 
programmer
                /// to construct the entry's header "by hand". File is set to 
null.
                /// </summary>
                public static TarEntry CreateTarEntry(string name)
                {
                        TarEntry entry = new TarEntry();
                        entry.Initialize();
                        entry.NameTarHeader(entry.header, name);
                        return entry;
                }
                
                /// <summary>
                /// Construct an entry for a file. File is set to file, and the
                /// header is constructed from information from the file.
                /// </summary>
                /// <param name = "fileName">
                /// The file that the entry represents.
                /// </param>
                public static TarEntry CreateEntryFromFile(string fileName)
                {
                        TarEntry entry = new TarEntry();
                        entry.Initialize();
                        entry.GetFileTarHeader(entry.header, fileName);
                        return entry;
                }
                
                /// <summary>
                /// Initialization code common to all constructors.
                /// </summary>
                void Initialize()
                {
                        this.file   = null;
                        this.header = new TarHeader();
                }
                
                /// <summary>
                /// Determine if the two entries are equal. Equality is 
determined
                /// by the header names being equal.
                /// </summary>
                /// <returns>
                /// True if the entries are equal.
                /// </returns>
                public override bool Equals(object it)
                {
                        if (!(it is TarEntry)) 
                        {
                                return false;
                        }
                        return 
this.header.name.ToString().Equals(((TarEntry)it).header.name.ToString());
                }
                
                /// <summary>
                /// Must be overridden when you override Equals.
                /// </summary>
                public override int GetHashCode()
                {
                        return this.header.name.ToString().GetHashCode();
                }
                
                
                /// <summary>
                /// 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.
                /// </summary>
                /// <param name = "desc">
                /// Entry to be checked as a descendent of this.
                /// </param>
                /// <returns>
                /// True if entry is a descendant of this.
                /// </returns>
                public bool IsDescendent(TarEntry desc)
                {
                        return 
desc.header.name.ToString().StartsWith(this.header.name.ToString());
                }
                
                /// <summary>
                /// Get this entry's header.
                /// </summary>
                /// <returns>
                /// This entry's TarHeader.
                /// </returns>
                public TarHeader TarHeader 
                {
                        get 
                        {
                                return this.header;
                        }
                }
                
                /// <summary>
                /// Get/Set this entry's name.
                /// </summary>
                public string Name 
                {
                        get 
                        {
                                return this.header.name.ToString();
                        }
                        set 
                        {
                                this.header.name = new StringBuilder(value);
                        }
                }
                
                /// <summary>
                /// Get/set this entry's user id.
                /// </summary>
                public int UserId 
                {
                        get 
                        {
                                return this.header.userId;
                        }
                        set 
                        {
                                this.header.userId = value;
                        }
                }
                
                /// <summary>
                /// Get/set this entry's group id.
                /// </summary>
                public int GroupId 
                {
                        get 
                        {
                                return this.header.groupId;
                        }
                        set 
                        {
                                this.header.groupId = value;
                        }
                }
                
                /// <summary>
                /// Get/set this entry's user name.
                /// </summary>
                public string UserName 
                {
                        get 
                        {
                                return this.header.userName.ToString();
                        }
                        set 
                        {
                                this.header.userName = new StringBuilder(value);
                        }
                }
                
                /// <summary>
                /// Get/set this entry's group name.
                /// </summary>
                public string GroupName 
                {
                        get 
                        {
                                return this.header.groupName.ToString();
                        }
                        set 
                        {
                                this.header.groupName = new 
StringBuilder(value);
                        }
                }
                
                /// <summary>
                /// Convenience method to set this entry's group and user ids.
                /// </summary>
                /// <param name="userId">
                /// This entry's new user id.
                /// </param>
                /// <param name="groupId">
                /// This entry's new group id.
                /// </param>
                public void SetIds(int userId, int groupId)
                {
                        UserId  = userId; 
                        GroupId = groupId;
                }
                
                /// <summary>
                /// Convenience method to set this entry's group and user names.
                /// </summary>
                /// <param name="userName">
                /// This entry's new user name.
                /// </param>
                /// <param name="groupName">
                /// This entry's new group name.
                /// </param>
                public void SetNames(string userName, string groupName)
                {
                        UserName  = userName;
                        GroupName = groupName;
                }

                //      TODO :
                //              /**
                //              * 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;
                //              }
                
                /// Convert time to DateTimes
                /**
                * Get/Set this entry's modification time.
                *
                * @param time This entry's new modification time.
                */
                public DateTime ModTime 
                {
                        get 
                        {
                                return this.header.modTime;
                        }
                        set 
                        {
                                this.header.modTime = value;
                        }
                }
                
                /// <summary>
                /// Get this entry's file.
                /// </summary>
                /// <returns>
                /// This entry's file.
                /// </returns>
                public string File 
                {
                        get 
                        {
                                return this.file;
                        }
                }
                
                /// <summary>
                /// Get/set this entry's file size.
                /// </summary>
                public long Size 
                {
                        get 
                        {
                                return this.header.size;
                        }
                        set 
                        {
                                this.header.size = value;
                        }
                }
                
                /// <summary>
                /// Convenience method that will modify an entry's name directly
                /// in place in an entry header buffer byte array.
                /// </summary>
                /// <param name="outbuf">
                /// The buffer containing the entry header to modify.
                /// </param>
                /// <param name="newName">
                /// The new name to place into the header buffer.
                /// </param>
                public void AdjustEntryName(byte[] outbuf, string newName)
                {
                        int offset = 0;
                        offset = TarHeader.GetNameBytes(new 
StringBuilder(newName), outbuf, offset, TarHeader.NAMELEN);
                }
                
                /// <summary>
                /// Return whether or not this entry represents a directory.
                /// </summary>
                /// <returns>
                /// True if this entry is a directory.
                /// </returns>
                public bool IsDirectory
                {
                        get 
                        {
                                if (this.file != null) 
                                {
                                        return Directory.Exists(file);
                                }
                                
                                if (this.header != null) 
                                {
                                        if (this.header.linkFlag == 
TarHeader.LF_DIR || this.header.name.ToString().EndsWith( "/" )) 
                                        {
                                                return true;
                                        }
                                }
                                return false;
                        }
                }
                
                /// <summary>
                /// Fill in a TarHeader with information from a File.
                /// </summary>
                /// <param name="hdr">
                /// The TarHeader to fill in.
                /// </param>
                /// <param name="file">
                /// The file from which to get the header information.
                /// </param>
                public void GetFileTarHeader(TarHeader hdr, string file)
                {
                        this.file = file;
                        
                        // bugfix from torhovl from #D forum:
                        string name = file;
                        //string name = Path.GetDirectoryName(file);
                        
                        if (Path.DirectorySeparatorChar == '\\') 
                        { // check if the OS is a windows
                                // Strip off drive letters!
                                if (name.Length > 2) 
                                {
                                        char ch1 = name[0];
                                        char ch2 = name[1];
                                        
                                        if (ch2 == ':' && Char.IsLetter(ch1)) 
                                        {
                                                name = name.Substring(2);
                                        }
                                }
                        }
                        
                        name = name.Replace(Path.DirectorySeparatorChar, '/');
                        
                        // No absolute pathnames
                        // Windows (and Posix?) paths can start with 
"\\NetworkDrive\",
                        // so we loop on starting /'s.
                        while (name.StartsWith("/")) {
                                name = name.Substring(1);
                        }
                        
                        hdr.linkName = new StringBuilder(String.Empty);
                        hdr.name     = new StringBuilder(name);
                        
                        if (Directory.Exists(file)) {
                                hdr.mode     = 040755; // Magic number for 
security access for a UNIX filesystem
                                hdr.linkFlag = TarHeader.LF_DIR;
                                if (hdr.name.Length==0 || 
hdr.name[hdr.name.Length - 1] != '/') { // now
                                        hdr.name.Append("/");
                                }
                                
                                hdr.size     = 0;
                        } else {
                                hdr.mode     = 0100644; // Magic number for 
security access for a UNIX filesystem
                                hdr.linkFlag = TarHeader.LF_NORMAL;
                                //Console.WriteLine(file.Replace('/', 
Path.DirectorySeparatorChar));
                        #if !ECMA_COMPAT
                                hdr.size     = new FileInfo(file.Replace('/', 
Path.DirectorySeparatorChar)).Length;
                        #endif
                        }
                        
                        // UNDONE When File lets us get the userName, use it!
                        hdr.modTime = 
System.IO.File.GetLastAccessTime(file.Replace('/', 
Path.DirectorySeparatorChar));
                        hdr.checkSum = 0;
                        hdr.devMajor = 0;
                        hdr.devMinor = 0;
                }
                
                /// <summary>
                /// If this entry represents a file, and the file is a 
directory, return
                /// an array of TarEntries for this entry's children.
                /// </summary>
                /// <returns>
                /// An array of TarEntry's for this entry's children.
                /// </returns>
                public TarEntry[] GetDirectoryEntries()
                {
                        if (this.file == null || !Directory.Exists(this.file)) 
                        {
                                return new TarEntry[0];
                        }
                        
                        string[]   list   = 
Directory.GetFileSystemEntries(this.file);
                        TarEntry[] result = new TarEntry[list.Length];
                        
                        string dirName = file;
                        if 
(!dirName.EndsWith(Path.DirectorySeparatorChar.ToString())) 
                        {
                                dirName += Path.DirectorySeparatorChar;
                        }
                        
                        for (int i = 0; i < list.Length; ++i) 
                        {
                                result[i] = 
TarEntry.CreateEntryFromFile(list[i]);
                        }
                        
                        return result;
                }
                
                /// <summary>
                /// Compute the checksum of a tar entry header.
                /// </summary>
                /// <param name = "buf">
                /// The tar entry's header buffer.
                /// </param>
                /// <returns>
                /// The computed checksum.
                /// </returns>
                public long ComputeCheckSum(byte[] buf)
                {
                        long sum = 0;
                        for (int i = 0; i < buf.Length; ++i) 
                        {
                                sum += 255 & buf[i]; // TODO : I think the 255 
& x isn't neccessary += buf[i] should be enough. CHECK IT!
                        }
                        return sum;
                }
                
                /// <summary>
                /// Write an entry's header information to a header buffer.
                /// </summary>
                /// <param name = "outbuf">
                /// The tar entry header buffer to fill in.
                /// </param>
                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(GetCTime(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);
                }
                
                // time conversion functions
                readonly static long     timeConversionFactor = 10000000L;
                readonly static DateTime datetTime1970        = new 
DateTime(1970, 1, 1, 0, 0, 0, 0);
                
                static int GetCTime(System.DateTime dateTime)
                {
                        return (int)((dateTime.Ticks - datetTime1970.Ticks) / 
timeConversionFactor);
                }
                
                static DateTime GetDateTimeFromCTime(long ticks)
                {
                        return new DateTime(datetTime1970.Ticks + ticks * 
timeConversionFactor);
                }
                
                /// <summary>
                /// Parse an entry's TarHeader information from a header buffer.
                /// </summary>
                /// <param name ="hdr">
                /// Parse an entry's TarHeader information from a header buffer.
                /// </param>
                /// <param name = "header">
                /// The tar entry header buffer to get information from.
                /// </param>
                public void ParseTarHeader(TarHeader hdr, byte[] header)
                {
                        int offset = 0;
                        
                        hdr.name = TarHeader.ParseName(header, offset, 
TarHeader.NAMELEN);
                        
                        offset += TarHeader.NAMELEN;
                        
                        hdr.mode = (int)TarHeader.ParseOctal(header, offset, 
TarHeader.MODELEN);
                        
                        offset += TarHeader.MODELEN;
                        
                        hdr.userId = (int)TarHeader.ParseOctal(header, offset, 
TarHeader.UIDLEN);
                        
                        offset += TarHeader.UIDLEN;
                        
                        hdr.groupId = (int)TarHeader.ParseOctal(header, offset, 
TarHeader.GIDLEN);
                        
                        offset += TarHeader.GIDLEN;
                        
                        hdr.size = TarHeader.ParseOctal(header, offset, 
TarHeader.SIZELEN);
                        
                        offset += TarHeader.SIZELEN;
                        
                        hdr.modTime = 
GetDateTimeFromCTime(TarHeader.ParseOctal(header, offset, 
TarHeader.MODTIMELEN));
                        
                        offset += TarHeader.MODTIMELEN;
                        
                        hdr.checkSum = (int)TarHeader.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 = (int)TarHeader.ParseOctal(header, 
offset, TarHeader.DEVLEN);
                        
                        offset += TarHeader.DEVLEN;
                        
                        hdr.devMinor = (int)TarHeader.ParseOctal(header, 
offset, TarHeader.DEVLEN);
                }
                
                /// <summary>
                /// Fill in a TarHeader given only the entry's name.
                /// </summary>
                /// <param name="hdr">
                /// The TarHeader to fill in.
                /// </param>
                /// <param name="name">
                /// The tar entry name.
                /// </param>
                public void NameTarHeader(TarHeader hdr, string name)
                {
                        bool isDir = name.EndsWith("/");
                        
                        hdr.checkSum = 0;
                        hdr.devMajor = 0;
                        hdr.devMinor = 0;
                        
                        hdr.name = new StringBuilder(name);
                        hdr.mode = isDir ? 040755 : 0100644; // TODO : I think 
I've seen these magics before ...
                        hdr.userId   = 0;
                        hdr.groupId  = 0;
                        hdr.size     = 0;
                        hdr.checkSum = 0;
                        
                        hdr.modTime  = DateTime.Now;//(new 
java.util.Date()).getTime() / 1000;
                        
                        hdr.linkFlag = isDir ? TarHeader.LF_DIR : 
TarHeader.LF_NORMAL;
                        
                        hdr.linkName  = new StringBuilder(String.Empty);
                        hdr.userName  = new StringBuilder(String.Empty);
                        hdr.groupName = new StringBuilder(String.Empty);
                        
                        hdr.devMajor = 0;
                        hdr.devMinor = 0;
                }
        }
}
/* The original Java file had this header:
        *
        ** Authored by Timothy Gerard Endres
        ** <mailto:address@hidden>  <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.
        **
        */

--- NEW FILE: TarBuffer.cs ---
// TarBuffer.cs
// Copyright (C) 2001 Mike Krueger
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
// Linking this library statically or dynamically with other modules is
// making a combined work based on this library.  Thus, the terms and
// conditions of the GNU General Public License cover the whole
// combination.
//
// As a special exception, the copyright holders of this library give you
// permission to link this library with independent modules to produce an
// executable, regardless of the license terms of these independent
// modules, and to copy and distribute the resulting executable under
// terms of your choice, provided that you also meet, for each linked
// independent module, the terms and conditions of the license of that
// module.  An independent module is a module which is not derived from
// or based on this library.  If you modify this library, you may extend
// this exception to your version of the library, but you are not
// obligated to do so.  If you do not wish to do so, delete this
// exception statement from your version.

using System;
using System.IO;
using System.Text;

namespace ICSharpCode.SharpZipLib.Tar 
{
        
        /// <summary>
        /// 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
        /// C# 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.
        /// </p>
        /// </summary>
        public class TarBuffer
        {
                public static readonly int DEFAULT_RCDSIZE = 512;
                public static readonly int DEFAULT_BLKSIZE = DEFAULT_RCDSIZE * 
20;
                
                Stream inputStream;
                Stream outputStream;
                
                byte[] blockBuffer;
                int    currBlkIdx;
                int    currRecIdx;
                int    blockSize;
                int    recordSize;
                int    recsPerBlock;
                
                bool   debug;
                
                protected TarBuffer()
                {
                }
                
                public static TarBuffer CreateInputTarBuffer(Stream inputStream)
                {
                        return CreateInputTarBuffer(inputStream, 
TarBuffer.DEFAULT_BLKSIZE);
                }
                public static TarBuffer CreateInputTarBuffer(Stream 
inputStream, int blockSize )
                {
                        return CreateInputTarBuffer(inputStream, blockSize, 
TarBuffer.DEFAULT_RCDSIZE);
                }
                public static TarBuffer CreateInputTarBuffer(Stream 
inputStream, int blockSize, int recordSize)
                {
                        TarBuffer tarBuffer = new TarBuffer();
                        tarBuffer.inputStream  = inputStream;
                        tarBuffer.outputStream = null;
                        tarBuffer.Initialize(blockSize, recordSize);
                        
                        return tarBuffer;
                }

                public static TarBuffer CreateOutputTarBuffer(Stream 
outputStream)
                {
                        return CreateOutputTarBuffer(outputStream, 
TarBuffer.DEFAULT_BLKSIZE);
                }
                public static TarBuffer CreateOutputTarBuffer(Stream 
outputStream, int blockSize )
                {
                        return CreateOutputTarBuffer(outputStream, blockSize, 
TarBuffer.DEFAULT_RCDSIZE);
                }
                public static TarBuffer CreateOutputTarBuffer(Stream 
outputStream, int blockSize, int recordSize)
                {
                        TarBuffer tarBuffer = new TarBuffer();
                        tarBuffer.inputStream  = null;
                        tarBuffer.outputStream = outputStream;
                        tarBuffer.Initialize(blockSize, recordSize);
                        
                        return tarBuffer;
                }
                
                /// <summary>
                /// Initialization common to all constructors.
                /// </summary>
                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 (inputStream != null) 
                        {
                                this.currBlkIdx = -1;
                                this.currRecIdx = this.recsPerBlock;
                        } 
                        else 
                        {
                                this.currBlkIdx = 0;
                                this.currRecIdx = 0;
                        }
                }
                
                /// <summary>
                /// Get the TAR Buffer's block size. Blocks consist of multiple 
records.
                /// </summary>
                public int GetBlockSize()
                {
                        return this.blockSize;
                }
                
                /// <summary>
                /// Get the TAR Buffer's record size.
                /// </summary>
                public int GetRecordSize()
                {
                        return this.recordSize;
                }
                
                /// <summary>
                /// Set the debugging flag for the buffer.
                /// </summary>
                public void SetDebug(bool debug)
                {
                        this.debug = debug;
                }
                
                /// <summary>
                /// Determine if an archive record indicate End of Archive. End 
of
                /// archive is indicated by a record that consists entirely of 
null bytes.
                /// </summary>
                /// <param name = "record">
                /// The record data to check.
                /// </param>
                public bool IsEOFRecord(byte[] record)
                {
                        for (int i = 0, sz = this.GetRecordSize(); i < sz; ++i) 
                        {
                                if (record[i] != 0) 
                                {
                                        return false;
                                }
                        }
                        
                        return true;
                }
                
                /// <summary>
                /// Skip over a record on the input stream.
                /// </summary>
                public void SkipRecord()
                {
                        if (this.debug) 
                        {
                                //Console.WriteLine.WriteLine("SkipRecord: 
recIdx = " + this.currRecIdx + " blkIdx = " + this.currBlkIdx);
                        }
                        
                        if (this.inputStream == null) 
                        {
                                throw new System.IO.IOException("no input 
stream defined");
                        }
                        
                        if (this.currRecIdx >= this.recsPerBlock) 
                        {
                                if (!this.ReadBlock()) 
                                {
                                        return; // UNDONE
                                }
                        }
                        
                        this.currRecIdx++;
                }
                
                /// <summary>
                /// Read a record from the input stream and return the data.
                /// </summary>
                /// <returns>
                /// The record data.
                /// </returns>
                public byte[] ReadRecord()
                {
                        if (this.debug) 
                        {
                                //Console.WriteLine.WriteLine( "ReadRecord: 
recIdx = " + this.currRecIdx + " blkIdx = " + this.currBlkIdx );
                        }
                        
                        if (this.inputStream == null) 
                        {
                                throw new System.IO.IOException("no input 
stream defined");
                        }
                        
                        if (this.currRecIdx >= this.recsPerBlock) 
                        {
                                if (!this.ReadBlock()) 
                                {
                                        return null;
                                }
                        }
                        
                        byte[] result = new byte[this.recordSize];
                        
                        Array.Copy(this.blockBuffer, (this.currRecIdx * 
this.recordSize), result, 0, this.recordSize );
                        this.currRecIdx++;
                        return result;
                }
                
                /// <returns>
                /// false if End-Of-File, else true
                /// </returns>
                bool ReadBlock()
                {
                        //Console.WriteLine(this.debug);
                        if (this.debug) 
                        {
                                //Console.WriteLine.WriteLine("ReadBlock: 
blkIdx = " + this.currBlkIdx);
                        }
                        
                        if (this.inputStream == null) 
                        {
                                throw new System.IO.IOException("no input 
stream stream defined");
                        }
                                                
                        this.currRecIdx = 0;
                        
                        int offset = 0;
                        int bytesNeeded = this.blockSize;
                        for (; bytesNeeded > 0 ;) 
                        {
                                long numBytes = 
this.inputStream.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 'address@hidden' for this fix.
                                //
                                if (numBytes <= 0) 
                                {
                                        break;
                                }
                                
                                offset      += (int)numBytes;
                                bytesNeeded -= (int)numBytes;
                                if (numBytes != this.blockSize) 
                                {
                                        if (this.debug) 
                                        {
                                                
//Console.WriteLine.WriteLine("ReadBlock: INCOMPLETE READ " + numBytes + " of " 
+ this.blockSize + " bytes read.");
                                        }
                                }
                        }
                        
                        this.currBlkIdx++;
                        return true;
                }
                
                /// <summary>
                /// Get the current block number, zero based.
                /// </summary>
                /// <returns>
                /// The current zero based block number.
                /// </returns>
                public int GetCurrentBlockNum()
                {
                        return this.currBlkIdx;
                }
                
                /// <summary>
                /// Get the current record number, within the current block, 
zero based.
                /// Thus, current offset = (currentBlockNum * recsPerBlk) + 
currentRecNum.
                /// </summary>
                /// <returns>
                /// The current zero based record number.
                /// </returns>
                public int GetCurrentRecordNum()
                {
                        return this.currRecIdx - 1;
                }
                
                /// <summary>
                /// Write an archive record to the archive.
                /// </summary>
                /// <param name="record">
                /// The record data to write to the archive.
                /// </param>
                /// 
                public void WriteRecord(byte[] record)
                {
                        if (this.debug) 
                        {
                                //Console.WriteLine.WriteLine("WriteRecord: 
recIdx = " + this.currRecIdx + " blkIdx = " + this.currBlkIdx );
                        }
                        
                        if (this.outputStream == null) 
                        {
                                throw new System.IO.IOException("no output 
stream defined");
                        }
                                                
                        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();
                        }
                        Array.Copy(record, 0, this.blockBuffer, 
(this.currRecIdx * this.recordSize), this.recordSize );
                        this.currRecIdx++;
                }
                
                /// <summary>
                /// 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.
                /// </summary>
                /// <param name="buf">
                /// The buffer containing the record data to write.
                /// </param>
                /// <param name="offset">
                /// The offset of the record data within buf.
                /// </param>
                public void WriteRecord(byte[] buf, int offset)
                {
                        if (this.debug) 
                        {
                                //Console.WriteLine.WriteLine("WriteRecord: 
recIdx = " + this.currRecIdx + " blkIdx = " + this.currBlkIdx );
                        }
                        
                        if (this.outputStream == null) 
                        {
                                throw new System.IO.IOException("no output 
stream stream defined");
                        }
                                                
                        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();
                        }
                        
                        Array.Copy(buf, offset, this.blockBuffer, 
(this.currRecIdx * this.recordSize), this.recordSize );
                        
                        this.currRecIdx++;
                }
                
                /// <summary>
                /// Write a TarBuffer block to the archive.
                /// </summary>
                void WriteBlock()
                {
                        if (this.debug) 
                        {
                                //Console.WriteLine.WriteLine("WriteBlock: 
blkIdx = " + this.currBlkIdx);
                        }
                        
                        if (this.outputStream == null) 
                        {
                                throw new System.IO.IOException("no output 
stream defined");
                        }
                        
                        this.outputStream.Write(this.blockBuffer, 0, 
this.blockSize);
                        this.outputStream.Flush();
                        
                        this.currRecIdx = 0;
                        this.currBlkIdx++;
                }
                
                /// <summary>
                /// Flush the current data block if it has any data in it.
                /// </summary>
                void Flush()
                {
                        if (this.debug) 
                        {
                                
//Console.WriteLine.WriteLine("TarBuffer.FlushBlock() called.");
                        }
                        
                        if (this.outputStream == null) 
                        {
                                throw new System.IO.IOException("no output base 
stream defined");
                        }
                        
                        if (this.currRecIdx > 0) 
                        {
                                this.WriteBlock();
                        }
                        outputStream.Flush();
                }
                
                /// <summary>
                /// Close the TarBuffer. If this is an output buffer, also 
flush the
                /// current block before closing.
                /// </summary>
                public void Close()
                {
                        if (this.debug) 
                        {
                                
//Console.WriteLine.WriteLine("TarBuffer.Close().");
                        }
                        
                        if (outputStream != null ) 
                        {
                                Flush();
        
                                //                              if ( 
this.outStream != System.out
                                //                                              
&& this.outStream != System.err ) {
                                outputStream.Close();
                                outputStream = null;
                        } 
                        else if (inputStream != null) 
                        {
                                //                              if 
(this.inStream != System.in ) {
                                inputStream.Close();
                                inputStream = null;
                        }
                }
        }
}
/* The original Java file had this header:
        *
        ** Authored by Timothy Gerard Endres
        ** <mailto:address@hidden>  <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.
        **
        */

--- NEW FILE: InvalidHeaderException.cs ---
// BZip2OutputStream.cs
// Copyright (C) 2001 Mike Krueger
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
// Linking this library statically or dynamically with other modules is
// making a combined work based on this library.  Thus, the terms and
// conditions of the GNU General Public License cover the whole
// combination.
// 
// As a special exception, the copyright holders of this library give you
// permission to link this library with independent modules to produce an
// executable, regardless of the license terms of these independent
// modules, and to copy and distribute the resulting executable under
// terms of your choice, provided that you also meet, for each linked
// independent module, the terms and conditions of the license of that
// module.  An independent module is a module which is not derived from
// or based on this library.  If you modify this library, you may extend
// this exception to your version of the library, but you are not
// obligated to do so.  If you do not wish to do so, delete this
// exception statement from your version.

using System;
using System.IO;

using ICSharpCode.SharpZipLib.Checksums;

namespace ICSharpCode.SharpZipLib.Tar {
        
        /// <summary>
        /// This exception is used to indicate that there is a problem
        /// with a TAR archive header.
        /// </summary>
        public class InvalidHeaderException : System.IO.IOException
        {
                public InvalidHeaderException()
                {
                }
        
                public InvalidHeaderException(string msg) : base(msg)
                {
                }
        }
}

/* The original Java file had this header:
** Authored by Timothy Gerard Endres
** <mailto:address@hidden>  <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. 
** 
*/


--- NEW FILE: TarArchive.cs ---
// TarInputStream.cs
// Copyright (C) 2001 Mike Krueger
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
// Linking this library statically or dynamically with other modules is
// making a combined work based on this library.  Thus, the terms and
// conditions of the GNU General Public License cover the whole
// combination.
//
// As a special exception, the copyright holders of this library give you
// permission to link this library with independent modules to produce an
// executable, regardless of the license terms of these independent
// modules, and to copy and distribute the resulting executable under
// terms of your choice, provided that you also meet, for each linked
// independent module, the terms and conditions of the license of that
// module.  An independent module is a module which is not derived from
// or based on this library.  If you modify this library, you may extend
// this exception to your version of the library, but you are not
// obligated to do so.  If you do not wish to do so, delete this
// exception statement from your version.

using System;
using System.IO;
using System.Text;

namespace ICSharpCode.SharpZipLib.Tar {
        
        public delegate void ProgressMessageHandler(TarArchive archive, string 
message);
        
        /// <summary>
        /// The TarArchive class implements the concept of a
        /// tar archive. A tar archive is a series of entries, each of
        /// which represents a file system object. Each entry in
        /// the archive consists of a header record. Directory entries
        /// consist only of the header record, and are followed by entries
        /// for the directory's contents. File entries consist of a
        /// header record followed by the number of records needed to
        /// contain the file's contents. All entries are written on
        /// record boundaries. Records are 512 bytes long.
        /// 
        /// TarArchives are instantiated in either read or write mode,
        /// based upon whether they are instantiated with an InputStream
        /// or an OutputStream. Once instantiated TarArchives read/write
        /// mode can not be changed.
        /// 
        /// There is currently no support for random access to tar archives.
        /// However, it seems that subclassing TarArchive, and using the
        /// TarBuffer.getCurrentRecordNum() and TarBuffer.getCurrentBlockNum()
        /// methods, this would be rather trvial.
        /// </summary>
        public class TarArchive
        {
                protected bool verbose;
                protected bool debug;
                protected bool keepOldFiles;
                protected bool asciiTranslate;
                
                protected int    userId;
                protected string userName;
                protected int    groupId;
                protected string groupName;
                
                protected string rootPath;
                protected string pathPrefix;
                
                protected int    recordSize;
                protected byte[] recordBuf;
                
                protected TarInputStream  tarIn;
                protected TarOutputStream tarOut;
                
                public event ProgressMessageHandler ProgressMessageEvent;
                
                protected virtual void OnProgressMessageEvent(string message)
                {
                        if (ProgressMessageEvent != null) {
                                ProgressMessageEvent(this, message);
                        }
                }
                
                protected TarArchive()
                {
                }
                
                /// <summary>
                /// The InputStream based constructors create a TarArchive for 
the
                /// purposes of e'x'tracting or lis't'ing a tar archive. Thus, 
use
                /// these constructors when you wish to extract files from or 
list
                /// the contents of an existing tar archive.
                /// </summary>
                public static TarArchive CreateInputTarArchive(Stream 
inputStream)
                {
                        return CreateInputTarArchive(inputStream, 
TarBuffer.DEFAULT_BLKSIZE);
                }
                
                public static TarArchive CreateInputTarArchive(Stream 
inputStream, int blockSize)
                {
                        return CreateInputTarArchive(inputStream, blockSize, 
TarBuffer.DEFAULT_RCDSIZE);
                }
                
                public static TarArchive CreateInputTarArchive(Stream 
inputStream, int blockSize, int recordSize)
                {
                        TarArchive archive = new TarArchive();
                        archive.tarIn = new TarInputStream(inputStream, 
blockSize, recordSize);
                        archive.Initialize(recordSize);
                        return archive;
                }
                
                /// <summary>
                /// The OutputStream based constructors create a TarArchive for 
the
                /// purposes of 'c'reating a tar archive. Thus, use these 
constructors
                /// when you wish to create a new tar archive and write files 
into it.
                /// </summary>
                public static TarArchive CreateOutputTarArchive(Stream 
outputStream)
                {
                        return CreateOutputTarArchive(outputStream, 
TarBuffer.DEFAULT_BLKSIZE);
                }
                
                public static TarArchive CreateOutputTarArchive(Stream 
outputStream, int blockSize)
                {
                        return CreateOutputTarArchive(outputStream, blockSize, 
TarBuffer.DEFAULT_RCDSIZE);
                }
                
                public static TarArchive CreateOutputTarArchive(Stream 
outputStream, int blockSize, int recordSize)
                {
                        TarArchive archive = new TarArchive();
                        archive.tarOut = new TarOutputStream(outputStream, 
blockSize, recordSize);
                        archive.Initialize(recordSize);
                        return archive;
                }
                
                
                /// <summary>
                /// Common constructor initialization code.
                /// </summary>
                void Initialize(int recordSize)
                {
                        this.rootPath   = null;
                        this.pathPrefix = null;
                        
//                      this.tempPath   = System.getProperty( "user.dir" );
                        
                        this.userId    = 0;
                        this.userName  = String.Empty;
                        this.groupId   = 0;
                        this.groupName = String.Empty;
                        
                        this.debug           = false;
                        this.verbose         = false;
                        this.keepOldFiles    = false;
                        
                        this.recordBuf = new byte[RecordSize];
                }
                
                /**
                * Set the debugging flag.
                *
                * @param debugF The new debug setting.
                */
                public void SetDebug(bool debugF)
                {
                        this.debug = debugF;
                        if (this.tarIn != null) {
                                this.tarIn.SetDebug(debugF);
                        } 
                        if (this.tarOut != null) {
                                this.tarOut.SetDebug(debugF);
                        }
                }
                
                /// <summary>
                /// Get/Set the verbosity setting.
                /// </summary>
                public bool IsVerbose {
                        get {
                                return verbose;
                        }
                        set {
                                verbose = value;
                        }
                }
                
                /// <summary>
                /// Set the flag that determines whether existing files are
                /// kept, or overwritten during extraction.
                /// </summary>
                /// <param name="keepOldFiles">
                /// If true, do not overwrite existing files.
                /// </param>
                public void SetKeepOldFiles(bool keepOldFiles)
                {
                        this.keepOldFiles = keepOldFiles;
                }
                
                /// <summary>
                /// Set the ascii file translation flag. If ascii file 
translatio
                /// is true, then the MIME file type will be consulted to 
determine
                /// if the file is of type 'text/*'. If the MIME type is not 
found,
                /// then the TransFileTyper is consulted if it is not null. If
                /// either of these two checks indicates the file is an ascii 
text
                /// file, it will be translated. The translation converts the 
local
                /// operating system's concept of line ends into the UNIX line 
end,
                /// '\n', which is the defacto standard for a TAR archive. This 
makes
                /// text files compatible with UNIX, and since most tar 
implementations
                /// text files compatible with UNIX, and since most tar 
implementations
                /// </summary>
                /// <param name= "asciiTranslate">
                /// If true, translate ascii text files.
                /// </param>
                public void SetAsciiTranslation(bool asciiTranslate)
                {
                        this.asciiTranslate = asciiTranslate;
                }
                
                /*
                /// <summary>
                /// Set the object that will determine if a file is of type
                /// ascii text for translation purposes.
                /// </summary>
                /// <param name="transTyper">
                /// The new TransFileTyper object.
                /// </param>
                public void SetTransFileTyper(TarTransFileTyper transTyper)
                {
                        this.transTyper = transTyper;
                }*/
                
                /// <summary>
                /// Set user and group information that will be used to fill in 
the
                /// tar archive's entry headers. Since Java currently provides 
no means
                /// of determining a user name, user id, group name, or group 
id for
                /// a given File, TarArchive allows the programmer to specify 
values
                /// to be used in their place.
                /// </summary>
                /// <param name="userId">
                /// The user Id to use in the headers.
                /// </param>
                /// <param name="userName">
                /// The user name to use in the headers.
                /// </param>
                /// <param name="groupId">
                /// The group id to use in the headers.
                /// </param>
                /// <param name="groupName">
                /// The group name to use in the headers.
                /// </param>
                public void SetUserInfo(int userId, string userName, int 
groupId, string groupName)
                {
                        this.userId    = userId;
                        this.userName  = userName;
                        this.groupId   = groupId;
                        this.groupName = groupName;
                }
                
                /// <summary>
                /// Get the user id being used for archive entry headers.
                /// </summary>
                /// <returns>
                /// The current user id.
                /// </returns>
                public int UserId {
                        get {
                                return this.userId;
                        }
                }
                
                /// <summary>
                /// Get the user name being used for archive entry headers.
                /// </summary>
                /// <returns>
                /// The current user name.
                /// </returns>
                public string UserName {
                        get {
                                return this.userName;
                        }
                }
                
                /// <summary>
                /// Get the group id being used for archive entry headers.
                /// </summary>
                /// <returns>
                /// The current group id.
                /// </returns>
                public int GroupId {
                        get {
                                return this.groupId;
                        }
                }
                
                /// <summary>
                /// Get the group name being used for archive entry headers.
                /// </summary>
                /// <returns>
                /// The current group name.
                /// </returns>
                public string GroupName {
                        get {
                                return this.groupName;
                        }
                }
                
                /// <summary>
                /// Get the archive's record size. Because of its history, tar
                /// supports the concept of buffered IO consisting of BLOCKS of
                /// RECORDS. This allowed tar to match the IO characteristics of
                /// the physical device being used. Of course, in the Java 
world,
                /// this makes no sense, WITH ONE EXCEPTION - archives are 
expected
                /// to be propertly "blocked". Thus, all of the horrible 
TarBuffer
                /// support boils down to simply getting the "boundaries" 
correct.
                /// </summary>
                /// <returns>
                /// The record size this archive is using.
                /// </returns>
                public int RecordSize {
                        get {
                                if (this.tarIn != null) {
                                        return this.tarIn.GetRecordSize();
                                } else if (this.tarOut != null) {
                                        return this.tarOut.GetRecordSize();
                                }
                                return TarBuffer.DEFAULT_RCDSIZE;
                        }
                }
                
                /// <summary>
                /// Close the archive. This simply calls the underlying
                /// tar stream's close() method.
                /// </summary>
                public void CloseArchive()
                {
                        if (this.tarIn != null) {
                                this.tarIn.Close();
                        } else if (this.tarOut != null) {
                                this.tarOut.Flush();
                                this.tarOut.Close();
                        }
                }
                
                /// <summary>
                /// Perform the "list" command and list the contents of the 
archive.
                /// 
                /// NOTE That this method uses the progress display to actually 
list
                /// the conents. If the progress display is not set, nothing 
will be
                /// listed!
                /// </summary>
                public void ListContents()
                {
                        while (true) {
                                TarEntry entry = this.tarIn.GetNextEntry();
                                
                                if (entry == null) {
                                        if (this.debug) {
                                                Console.Error.WriteLine("READ 
EOF RECORD");
                                        }
                                        break;
                                }
                                OnProgressMessageEvent(entry.Name);
                        }
                }
                
                /// <summary>
                /// Perform the "extract" command and extract the contents of 
the archive.
                /// </summary>
                /// <param name="destDir">
                /// The destination directory into which to extract.
                /// </param>
                public void ExtractContents(string destDir)
                {
                        while (true) {
                                TarEntry entry = this.tarIn.GetNextEntry();
                                
                                if (entry == null) {
                                        if (this.debug) {
                                                Console.Error.WriteLine("READ 
EOF RECORD");
                                        }
                                        break;
                                }
                                
                                this.ExtractEntry(destDir, entry);
                        }
                }
                
                void EnsureDirectoryExists(string directoryName)
                {
                #if !ECMA_COMPAT
                        if (!Directory.Exists(directoryName)) {
                                try {
                                        
Directory.CreateDirectory(directoryName);
                                } catch (Exception e) {
                                        throw new IOException("error making 
directory path '" + directoryName + "', " + e.Message);
                                }
                        }
                #endif
                }
                
                
                bool IsBinary(string filename)
                {
                        FileStream fs = File.OpenRead(filename);
                        
                        byte[] content = new byte[fs.Length];
                        
                        fs.Read(content, 0, (int)fs.Length);
                        fs.Close();
                        
                        // assume that ascii 0 or 
                        // ascii 255 are only found in non text files.
                        // and that all non text files contain 0 and 255
                        foreach (byte b in content) {
                                if (b == 0 || b == 255) {
                                        return true;
                                }
                        }
                        
                        return false;
                }               
                
                /// <summary>
                /// Extract an entry from the archive. This method assumes that 
the
                /// tarIn stream has been properly set with a call to 
getNextEntry().
                /// </summary>
                /// <param name="destDir">
                /// The destination directory into which to extract.
                /// </param>
                /// <param name="entry">
                /// The TarEntry returned by tarIn.getNextEntry().
                /// </param>
                void ExtractEntry(string destDir, TarEntry entry)
                {
                        if (this.verbose) {
                                OnProgressMessageEvent(entry.Name);
                        }
                        
                        string name = entry.Name;
                        name = name.Replace('/', Path.DirectorySeparatorChar);
                        
                        if 
(!destDir.EndsWith(Path.DirectorySeparatorChar.ToString())) {
                                destDir += Path.DirectorySeparatorChar;
                        }
                        
                        string destFile = destDir + name;
                        
                        if (entry.IsDirectory) {
                                EnsureDirectoryExists(destFile);
                        } else {
                                string parentDirectory = 
Path.GetDirectoryName(destFile);
                                EnsureDirectoryExists(parentDirectory);
                                
                                if (this.keepOldFiles && File.Exists(destFile)) 
{
                                        if (this.verbose) {
                                                OnProgressMessageEvent("not 
overwriting " + entry.Name);
                                        }
                                } else {
                                        bool asciiTrans = false;
                                        Stream outputStream = 
File.Create(destFile);
                                        if (this.asciiTranslate) {
                                                asciiTrans = 
!IsBinary(destFile);
// original java sourcecode : 
//                                              MimeType mime      = null;
//                                              string contentType = null;
//                                              try {
//                                                      contentType = 
FileTypeMap.getDefaultFileTypeMap().getContentType( destFile );
//                                                      
//                                                      mime = new 
MimeType(contentType);
//                                                      
//                                                      if 
(mime.getPrimaryType().equalsIgnoreCase( "text" )) {
//                                                              asciiTrans = 
true;
//                                                      } else if ( 
this.transTyper != null ) {
//                                                          if ( 
this.transTyper.isAsciiFile( entry.getName() ) ) {
//                                                              asciiTrans = 
true;
//                                                          }
//                                                      }
//                                              } catch (MimeTypeParseException 
ex) {
//                                              }
//                                              
//                                              if (this.debug) {
//                                                      
Console.Error.WriteLine(("EXTRACT TRANS? '" + asciiTrans + "'  ContentType='" + 
contentType + "'  PrimaryType='" + mime.getPrimaryType() + "'" );
//                                              }
                                        }
                                        
                                        StreamWriter outw = null;
                                        if (asciiTrans) {
                                                outw = new 
StreamWriter(outputStream);
                                        }
                                        
                                        byte[] rdbuf = new byte[32 * 1024];
                                        
                                        while (true) {
                                                int numRead = 
this.tarIn.Read(rdbuf, 0, rdbuf.Length);
                                                
                                                if (numRead <= 0) {
                                                        break;
                                                }
                                                
                                                if (asciiTrans) {
                                                        for (int off = 0, b = 
0; b < numRead; ++b) {
                                                                if (rdbuf[b] == 
10) {
                                                                        string 
s = Encoding.ASCII.GetString(rdbuf, off, (b - off));
                                                                        
outw.WriteLine(s);
                                                                        off = b 
+ 1;
                                                                }
                                                        }
                                                } else {
                                                        
outputStream.Write(rdbuf, 0, numRead);
                                                }
                                        }
                                        
                                        if (asciiTrans) {
                                                outw.Close();
                                        } else {
                                                outputStream.Close();
                                        }
                                }
                        }
                }
                
                /// <summary>
                /// Write an entry to the archive. This method will call the 
putNextEntry
                /// and then write the contents of the entry, and finally call 
closeEntry()()
                /// for entries that are files. For directories, it will call 
putNextEntry(),
                /// and then, if the recurse flag is true, process each entry 
that is a
                /// child of the directory.
                /// </summary>
                /// <param name="entry">
                /// The TarEntry representing the entry to write to the archive.
                /// </param>
                /// <param name="recurse">
                /// If true, process the children of directory entries.
                /// </param>
                public void WriteEntry(TarEntry entry, bool recurse)
                {
                        bool asciiTrans = false;
                        
                        string tempFileName = null;
                        string eFile        = entry.File;
                        
                        // Work on a copy of the entry so we can manipulate it.
                        // Note that we must distinguish how the entry was 
constructed.
                        //
                        if (eFile == null || eFile.Length == 0) {
                                entry = TarEntry.CreateTarEntry(entry.Name);
                        } else {
                                //
                                // The user may have explicitly set the entry's 
name to
                                // something other than the file's path, so we 
must save
                                // and restore it. This should work even when 
the name
                                // was set from the File's name.
                                //
                                string saveName = entry.Name;
                                entry = TarEntry.CreateEntryFromFile(eFile);
                                entry.Name = saveName;
                        }
                        
                        if (this.verbose) {
                                OnProgressMessageEvent(entry.Name);
                        }
                        
                        if (this.asciiTranslate && !entry.IsDirectory) {
                                asciiTrans = !IsBinary(eFile);
// original java source :
//                              MimeType mime = null;
//                              string contentType = null;
//      
//                              try {
//                                      contentType = 
FileTypeMap.getDefaultFileTypeMap(). getContentType( eFile );
//                                      
//                                      mime = new MimeType( contentType );
//                                      
//                                      if ( mime.getPrimaryType().
//                                          equalsIgnoreCase( "text" ) )
//                                          {
//                                              asciiTrans = true;
//                                          }
//                                          else if ( this.transTyper != null )
//                                          {
//                                              if ( 
this.transTyper.isAsciiFile( eFile ) )
//                                              {
//                                                      asciiTrans = true;
//                                              }
//                                          }
//                              } catch ( MimeTypeParseException ex )
//                              {
//      //                               IGNORE THIS ERROR...
//                              }
//                      
//                      if (this.debug) {
//                              Console.Error.WriteLine("CREATE TRANS? '" + 
asciiTrans + "'  ContentType='" + contentType + "'  PrimaryType='" + 
mime.getPrimaryType()+ "'" );
//                      }
                        
                        #if !ECMA_COMPAT
                        if (asciiTrans) {
                                tempFileName = Path.GetTempFileName();
                                
                                StreamReader inStream  = File.OpenText(eFile);
                                Stream       outStream = new 
BufferedStream(File.Create(tempFileName));
                                
                                while (true) {
                                        string line = inStream.ReadLine();
                                        if (line == null) {
                                                break;
                                        }
                                        byte[] data = 
Encoding.ASCII.GetBytes(line);
                                        outStream.Write(data, 0, data.Length);
                                        outStream.WriteByte((byte)'\n');
                                }
                                
                                inStream.Close();
                                outStream.Flush();
                                outStream.Close();
                                
                                entry.Size = new FileInfo(tempFileName).Length;
                                
                                eFile = tempFileName;
                        }
                        #endif
                        }
                    
                    string newName = null;
                
                        if (this.rootPath != null) {
                                if (entry.Name.StartsWith(this.rootPath)) {
                                        newName = 
entry.Name.Substring(this.rootPath.Length + 1 );
                                }
                        }
                        
                        if (this.pathPrefix != null) {
                                newName = (newName == null) ? this.pathPrefix + 
"/" + entry.Name : this.pathPrefix + "/" + newName;
                        }
                        
                        if (newName != null) {
                                entry.Name = newName;
                        }
                        
                        this.tarOut.PutNextEntry(entry);
                        
                        if (entry.IsDirectory) {
                                if (recurse) {
                                        TarEntry[] list = 
entry.GetDirectoryEntries();
                                        for (int i = 0; i < list.Length; ++i) {
                                                this.WriteEntry(list[i], 
recurse);
                                        }
                                }
                        } else {
                                Stream inputStream = File.OpenRead(eFile);
                                int numWritten = 0;
                                byte[] eBuf = new byte[32 * 1024];
                                while (true) {
                                        int numRead = inputStream.Read(eBuf, 0, 
eBuf.Length);
                                        
                                        if (numRead <=0) {
                                                break;
                                        }
                                        
                                        this.tarOut.Write(eBuf, 0, numRead);
                                        numWritten +=  numRead;
                                }
                                Console.WriteLine("written " + numWritten + " 
bytes");
                                
                                inputStream.Close();
                                
                                if (tempFileName != null && tempFileName.Length 
> 0) {
                                        File.Delete(tempFileName);
                                }
                                
                                this.tarOut.CloseEntry();
                        }
                }
        }
}
/* The original Java file had this header:
        ** Authored by Timothy Gerard Endres
        ** <mailto:address@hidden>  <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.
        **
        */

--- NEW FILE: TarOutputStream.cs ---
// TarOutputStream.cs
// Copyright (C) 2001 Mike Krueger
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
// Linking this library statically or dynamically with other modules is
// making a combined work based on this library.  Thus, the terms and
// conditions of the GNU General Public License cover the whole
// combination.
// 
// As a special exception, the copyright holders of this library give you
// permission to link this library with independent modules to produce an
// executable, regardless of the license terms of these independent
// modules, and to copy and distribute the resulting executable under
// terms of your choice, provided that you also meet, for each linked
// independent module, the terms and conditions of the license of that
// module.  An independent module is a module which is not derived from
// or based on this library.  If you modify this library, you may extend
// this exception to your version of the library, but you are not
// obligated to do so.  If you do not wish to do so, delete this
// exception statement from your version.

using System;
using System.IO;
using System.Text;

namespace ICSharpCode.SharpZipLib.Tar 
{
        
        /// <summary>
        /// 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().
        /// </summary>
        /// public
        public class TarOutputStream : Stream
        {
                protected bool   debug;
                protected int    currSize;
                protected int    currBytes;
                protected byte[] recordBuf;
                protected int    assemLen;
                protected byte[] assemBuf;
                
                protected TarBuffer buffer;
                protected Stream    outputStream;
                
                /// <summary>
                /// I needed to implement the abstract member.
                /// </summary>
                public override bool CanRead 
                {
                        get 
                        {
                                return outputStream.CanRead;
                        }
                }
                
                /// <summary>
                /// I needed to implement the abstract member.
                /// </summary>
                public override bool CanSeek 
                {
                        get 
                        {
                                return outputStream.CanSeek;
                        }
                }
                
                /// <summary>
                /// I needed to implement the abstract member.
                /// </summary>
                public override bool CanWrite 
                {
                        get 
                        {
                                return outputStream.CanWrite;
                        }
                }
                
                /// <summary>
                /// I needed to implement the abstract member.
                /// </summary>
                public override long Length 
                {
                        get 
                        {
                                return outputStream.Length;
                        }
                }
                
                /// <summary>
                /// I needed to implement the abstract member.
                /// </summary>
                public override long Position 
                {
                        get 
                        {
                                return outputStream.Position;
                        }
                        set 
                        {
                                outputStream.Position = value;
                        }
                }
                
                /// <summary>
                /// I needed to implement the abstract member.
                /// </summary>
                public override long Seek(long offset, SeekOrigin origin)
                {
                        return outputStream.Seek(offset, origin);
                }
                
                /// <summary>
                /// I needed to implement the abstract member.
                /// </summary>
                public override void SetLength(long val)
                {
                        outputStream.SetLength(val);
                }
                
                /// <summary>
                /// I needed to implement the abstract member.
                /// </summary>
                public override int ReadByte()
                {
                        return outputStream.ReadByte();
                }
                
                /// <summary>
                /// I needed to implement the abstract member.
                /// </summary>
                public override int Read(byte[] b, int off, int len)
                {
                        return outputStream.Read(b, off, len);
                }
                
                public override void Flush()
                {
                        outputStream.Flush();
                }
                                
                public TarOutputStream(Stream outputStream) : 
this(outputStream, TarBuffer.DEFAULT_BLKSIZE, TarBuffer.DEFAULT_RCDSIZE)
                {
                }
                
                public TarOutputStream(Stream outputStream, int blockSize) : 
this(outputStream, blockSize, TarBuffer.DEFAULT_RCDSIZE)
                {
                }
                
                public TarOutputStream(Stream outputStream, int blockSize, int 
recordSize)
                {
                        this.outputStream = outputStream;
                        this.buffer       = 
TarBuffer.CreateOutputTarBuffer(outputStream, blockSize, recordSize);
                        
                        this.debug     = false;
                        this.assemLen  = 0;
                        this.assemBuf  = new byte[recordSize];
                        this.recordBuf = new byte[recordSize];
                }
                
                /// <summary>
                /// Sets the debugging flag.
                /// </summary>
                /// <param name = "debugF">
                /// True to turn on debugging.
                /// </param>
                public void SetDebug(bool debugF)
                {
                        this.debug = debugF;
                        SetBufferDebug(debugF);
                }
                
                public void SetBufferDebug(bool debug)
                {
                        this.buffer.SetDebug(debug);
                }
                
                /// <summary>
                /// Ends the TAR archive without closing the underlying 
OutputStream.
                /// The result is that the EOF record of nulls is written.
                /// </summary>
                public void Finish()
                {
                        this.WriteEOFRecord();
                }
                
                /// <summary>
                /// Ends the TAR archive and closes the underlying OutputStream.
                /// This means that finish() is called followed by calling the
                /// TarBuffer's close().
                /// </summary>
                public override void Close()
                {
                        this.Finish();
                        this.buffer.Close();
                }
                
                /// <summary>
                /// Get the record size being used by this stream's TarBuffer.
                /// </summary>
                /// <returns>
                /// The TarBuffer record size.
                /// </returns>
                public int GetRecordSize()
                {
                        return this.buffer.GetRecordSize();
                }
                
                /// <summary>
                /// 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.
                /// </summary>
                /// <param name="entry">
                /// The TarEntry to be written to the archive.
                /// </param>
                public void PutNextEntry(TarEntry entry)
                {
                        if (entry.TarHeader.name.Length > TarHeader.NAMELEN) 
                        {
                                throw new InvalidHeaderException("file name '" 
+ entry.TarHeader.name + "' is too long ( > " + TarHeader.NAMELEN + " bytes )");
                        }
                        
                        entry.WriteEntryHeader(this.recordBuf);
                        this.buffer.WriteRecord(this.recordBuf);
                        
                        this.currBytes = 0;
                        
                        this.currSize = entry.IsDirectory ? 0 : (int)entry.Size;
                }
                
                /// <summary>
                /// 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.
                /// </summary>
                public void CloseEntry()
                {
                        if (this.assemLen > 0) 
                        {
                                for (int i = this.assemLen; i < 
this.assemBuf.Length; ++i) 
                                {
                                        this.assemBuf[i] = 0;
                                }
                                
                                this.buffer.WriteRecord(this.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");
                        }
                }
                
                /// <summary>
                /// Writes a byte to the current tar archive entry.
                /// This method simply calls Write(byte[], int, int).
                /// </summary>
                /// <param name="b">
                /// The byte written.
                /// </param>
                public override void WriteByte(byte b)
                {
                        this.Write(new byte[] { b }, 0, 1);
                }
                
                /// <summary>
                /// 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.
                /// </summary>
                /// <param name = "wBuf">
                /// The buffer to write to the archive.
                /// </param>
                /// <param name = "wOffset">
                /// The offset in the buffer from which to get bytes.
                /// </param>
                /// <param name = "numToWrite">
                /// The number of bytes to write.
                /// </param>
                public override void Write(byte[] wBuf, int wOffset, int 
numToWrite)
                {
                        if ((this.currBytes + numToWrite) > this.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 > 0) 
                        {
                                if ((this.assemLen + numToWrite ) >= 
this.recordBuf.Length) 
                                {
                                        int aLen = this.recordBuf.Length - 
this.assemLen;
                                        
                                        Array.Copy(this.assemBuf, 0, 
this.recordBuf, 0, this.assemLen);
                                        
                                        Array.Copy(wBuf, wOffset, 
this.recordBuf, this.assemLen, aLen);
                                        
                                        this.buffer.WriteRecord(this.recordBuf);
                                        
                                        this.currBytes += this.recordBuf.Length;
                                        
                                        wOffset    += aLen;
                                        numToWrite -= aLen;
                                        
                                        this.assemLen = 0;
                                } 
                                else 
                                {// ( (this.assemLen + numToWrite ) < 
this.recordBuf.length )
                                        Array.Copy(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)
                        //
                        while (numToWrite > 0) 
                        {
                                if (numToWrite < this.recordBuf.Length) 
                                {
                                        Array.Copy(wBuf, wOffset, 
this.assemBuf, this.assemLen, numToWrite);
                                        this.assemLen += numToWrite;
                                        break;
                                }
                                
                                this.buffer.WriteRecord(wBuf, wOffset);
                                
                                int num = this.recordBuf.Length;
                                this.currBytes += num;
                                numToWrite     -= num;
                                wOffset        += num;
                        }
                }
                
                /// <summary>
                /// Write an EOF (end of archive) record to the tar archive.
                /// An EOF record consists of a record of all zeros.
                /// </summary>
                void WriteEOFRecord()
                {
                        for (int i = 0; i < this.recordBuf.Length; ++i) 
                        {
                                this.recordBuf[i] = 0;
                        }
                        this.buffer.WriteRecord(this.recordBuf);
                }
        }
}

/* The original Java file had this header:
        ** Authored by Timothy Gerard Endres
        ** <mailto:address@hidden>  <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.
        **
        */





reply via email to

[Prev in Thread] Current Thread [Next in Thread]