[Top][All Lists]
[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.
**
*/
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [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,
Rhys Weatherley <address@hidden> <=
- Prev by Date:
[Dotgnu-pnet-commits] pnetlib/SharpZipLib/Zip ZipConstants.cs, NONE, 1.1 ZipEntry.cs, NONE, 1.1 ZipFile.cs, NONE, 1.1 ZipInputStream.cs, NONE, 1.1 ZipOutputStream.cs, NONE, 1.1
- Next by Date:
[Dotgnu-pnet-commits] pnetlib/SharpZipLib/GZip GZIPConstants.cs, NONE, 1.1 GzipInputStream.cs, NONE, 1.1 GzipOutputStream.cs, NONE, 1.1
- Previous by thread:
[Dotgnu-pnet-commits] pnetlib/SharpZipLib/Zip ZipConstants.cs, NONE, 1.1 ZipEntry.cs, NONE, 1.1 ZipFile.cs, NONE, 1.1 ZipInputStream.cs, NONE, 1.1 ZipOutputStream.cs, NONE, 1.1
- Next by thread:
[Dotgnu-pnet-commits] pnetlib/SharpZipLib/GZip GZIPConstants.cs, NONE, 1.1 GzipInputStream.cs, NONE, 1.1 GzipOutputStream.cs, NONE, 1.1
- Index(es):