Imported Upstream version 3.6.0

Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
This commit is contained in:
Jo Shields
2014-08-13 10:39:27 +01:00
commit a575963da9
50588 changed files with 8155799 additions and 0 deletions

View File

@@ -0,0 +1,226 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SharpCompress.Common;
#if PORTABLE
using SharpCompress.Common.Rar.Headers;
#endif
namespace SharpCompress.Reader
{
/// <summary>
/// A generic push reader that reads unseekable comrpessed streams.
/// </summary>
internal abstract class AbstractReader<TEntry, TVolume> : IReader, IExtractionListener
where TEntry : Entry
where TVolume : Volume
{
private bool completed;
private IEnumerator<TEntry> entriesForCurrentReadStream;
private bool wroteCurrentEntry;
public event EventHandler<CompressedBytesReadEventArgs> CompressedBytesRead;
public event EventHandler<FilePartExtractionBeginEventArgs> FilePartExtractionBegin;
internal AbstractReader(Options options, ArchiveType archiveType)
{
ArchiveType = archiveType;
Options = options;
}
internal Options Options { get; private set; }
public ArchiveType ArchiveType { get; private set; }
/// <summary>
/// Current volume that the current entry resides in
/// </summary>
public abstract TVolume Volume { get; }
/// <summary>
/// Current file entry
/// </summary>
public TEntry Entry
{
get { return entriesForCurrentReadStream.Current; }
}
#region IDisposable Members
public void Dispose()
{
if (entriesForCurrentReadStream != null)
{
entriesForCurrentReadStream.Dispose();
}
Volume.Dispose();
}
#endregion
public bool MoveToNextEntry()
{
if (completed)
{
return false;
}
if (entriesForCurrentReadStream == null)
{
return LoadStreamForReading(RequestInitialStream());
}
if (!wroteCurrentEntry)
{
SkipEntry();
}
wroteCurrentEntry = false;
if (NextEntryForCurrentStream())
{
return true;
}
completed = true;
return false;
}
internal bool LoadStreamForReading(Stream stream)
{
if (entriesForCurrentReadStream != null)
{
entriesForCurrentReadStream.Dispose();
}
if ((stream == null) || (!stream.CanRead))
{
throw new MultipartStreamRequiredException("File is split into multiple archives: '"
+ Entry.Key +
"'. A new readable stream is required. Use Cancel if it was intended.");
}
entriesForCurrentReadStream = GetEntries(stream).GetEnumerator();
if (entriesForCurrentReadStream.MoveNext())
{
return true;
}
return false;
}
internal virtual Stream RequestInitialStream()
{
return Volume.Stream;
}
internal virtual bool NextEntryForCurrentStream()
{
return entriesForCurrentReadStream.MoveNext();
}
internal abstract IEnumerable<TEntry> GetEntries(Stream stream);
#region Entry Skip/Write
private void SkipEntry()
{
if (!Entry.IsDirectory)
{
Skip();
}
}
private readonly byte[] skipBuffer = new byte[4096];
private void Skip()
{
if (!Entry.IsSolid)
{
var rawStream = Entry.Parts.First().GetRawStream();
if (rawStream != null)
{
var bytesToAdvance = Entry.CompressedSize;
for (var i = 0; i < bytesToAdvance / skipBuffer.Length; i++)
{
rawStream.Read(skipBuffer, 0, skipBuffer.Length);
}
rawStream.Read(skipBuffer, 0, (int)(bytesToAdvance % skipBuffer.Length));
return;
}
}
using (var s = OpenEntryStream())
{
while (s.Read(skipBuffer, 0, skipBuffer.Length) > 0)
{
}
}
}
public void WriteEntryTo(Stream writableStream)
{
if (wroteCurrentEntry)
{
throw new ArgumentException("WriteEntryTo or OpenEntryStream can only be called once.");
}
if ((writableStream == null) || (!writableStream.CanWrite))
{
throw new ArgumentNullException(
"A writable Stream was required. Use Cancel if that was intended.");
}
Write(writableStream);
wroteCurrentEntry = true;
}
internal void Write(Stream writeStream)
{
using (Stream s = OpenEntryStream())
{
s.TransferTo(writeStream);
}
}
public EntryStream OpenEntryStream()
{
if (wroteCurrentEntry)
{
throw new ArgumentException("WriteEntryTo or OpenEntryStream can only be called once.");
}
var stream = GetEntryStream();
wroteCurrentEntry = true;
return stream;
}
protected virtual EntryStream GetEntryStream()
{
return new EntryStream(Entry.Parts.First().GetCompressedStream());
}
#endregion
IEntry IReader.Entry
{
get { return Entry; }
}
void IExtractionListener.FireCompressedBytesRead(long currentPartCompressedBytes, long compressedReadBytes)
{
if (CompressedBytesRead != null)
{
CompressedBytesRead(this, new CompressedBytesReadEventArgs()
{
CurrentFilePartCompressedBytesRead = currentPartCompressedBytes,
CompressedBytesRead = compressedReadBytes
});
}
}
void IExtractionListener.FireFilePartExtractionBegin(string name, long size, long compressedSize)
{
if (FilePartExtractionBegin != null)
{
FilePartExtractionBegin(this, new FilePartExtractionBeginEventArgs()
{
CompressedSize = compressedSize,
Size = size,
Name = name,
});
}
}
}
}

View File

@@ -0,0 +1,95 @@
using System.IO;
using SharpCompress.Common;
namespace SharpCompress.Reader
{
internal static class IReaderExtensions
{
#if !PORTABLE && !NETFX_CORE
public static void WriteEntryTo(this IReader reader, string filePath)
{
using (Stream stream = File.Open(filePath, FileMode.Create, FileAccess.Write))
{
reader.WriteEntryTo(stream);
}
}
public static void WriteEntryTo(this IReader reader, FileInfo filePath)
{
using (Stream stream = filePath.Open(FileMode.Create))
{
reader.WriteEntryTo(stream);
}
}
/// <summary>
/// Extract all remaining unread entries to specific directory, retaining filename
/// </summary>
public static void WriteAllToDirectory(this IReader reader, string destinationDirectory,
ExtractOptions options = ExtractOptions.Overwrite)
{
while (reader.MoveToNextEntry())
{
reader.WriteEntryToDirectory(destinationDirectory, options);
}
}
/// <summary>
/// Extract to specific directory, retaining filename
/// </summary>
public static void WriteEntryToDirectory(this IReader reader, string destinationDirectory,
ExtractOptions options = ExtractOptions.Overwrite)
{
string destinationFileName = string.Empty;
string file = Path.GetFileName(reader.Entry.Key);
if (options.HasFlag(ExtractOptions.ExtractFullPath))
{
string folder = Path.GetDirectoryName(reader.Entry.Key);
string destdir = Path.Combine(destinationDirectory, folder);
if (!Directory.Exists(destdir))
{
Directory.CreateDirectory(destdir);
}
destinationFileName = Path.Combine(destdir, file);
}
else
{
destinationFileName = Path.Combine(destinationDirectory, file);
}
if (!reader.Entry.IsDirectory)
{
reader.WriteEntryToFile(destinationFileName, options);
}
else if (options.HasFlag(ExtractOptions.ExtractFullPath) && !Directory.Exists(destinationFileName))
{
Directory.CreateDirectory(destinationFileName);
}
}
/// <summary>
/// Extract to specific file
/// </summary>
public static void WriteEntryToFile(this IReader reader, string destinationFileName,
ExtractOptions options = ExtractOptions.Overwrite)
{
FileMode fm = FileMode.Create;
if (!options.HasFlag(ExtractOptions.Overwrite))
{
fm = FileMode.CreateNew;
}
using (FileStream fs = File.Open(destinationFileName, fm))
{
reader.WriteEntryTo(fs);
//using (Stream s = reader.OpenEntryStream())
//{
// s.TransferTo(fs);
//}
}
}
#endif
}
}

View File

@@ -0,0 +1,34 @@
using System;
using System.IO;
using SharpCompress.Common;
namespace SharpCompress.Reader
{
internal interface IReader : IDisposable
{
event EventHandler<CompressedBytesReadEventArgs> CompressedBytesRead;
event EventHandler<FilePartExtractionBeginEventArgs> FilePartExtractionBegin;
ArchiveType ArchiveType { get; }
IEntry Entry { get; }
/// <summary>
/// Decompresses the current entry to the stream. This cannot be called twice for the current entry.
/// </summary>
/// <param name="writableStream"></param>
void WriteEntryTo(Stream writableStream);
/// <summary>
/// Moves to the next entry by reading more data from the underlying stream. This skips if data has not been read.
/// </summary>
/// <returns></returns>
bool MoveToNextEntry();
/// <summary>
/// Opens the current entry as a stream that will decompress as it is read.
/// Read the entire stream or use SkipEntry on EntryStream.
/// </summary>
EntryStream OpenEntryStream();
}
}

View File

@@ -0,0 +1,106 @@
using System;
using System.IO;
#if GZIP
using SharpCompress.Archive.GZip;
#endif
#if RAR
using SharpCompress.Archive.Rar;
#endif
#if TAR
using SharpCompress.Archive.Tar;
#endif
using SharpCompress.Archive.Zip;
using SharpCompress.Common;
using SharpCompress.Compressor;
#if BZIP2
using SharpCompress.Compressor.BZip2;
#endif
using SharpCompress.IO;
#if GZIP
using SharpCompress.Reader.GZip;
#endif
#if RAR
using SharpCompress.Reader.Rar;
#endif
#if TAR
using SharpCompress.Reader.Tar;
#endif
using SharpCompress.Reader.Zip;
#if DEFLATE
using GZipStream = SharpCompress.Compressor.Deflate.GZipStream;
#endif
namespace SharpCompress.Reader
{
internal static class ReaderFactory
{
/// <summary>
/// Opens a Reader for Non-seeking usage
/// </summary>
/// <param name="stream"></param>
/// <param name="options"></param>
/// <returns></returns>
public static IReader Open(Stream stream, Options options = Options.KeepStreamsOpen)
{
stream.CheckNotNull("stream");
RewindableStream rewindableStream = new RewindableStream(stream);
rewindableStream.StartRecording();
if (ZipArchive.IsZipFile(rewindableStream, null))
{
rewindableStream.Rewind(true);
return ZipReader.Open(rewindableStream, null, options);
}
#if GZIP
rewindableStream.Rewind(false);
if (GZipArchive.IsGZipFile(rewindableStream))
{
rewindableStream.Rewind(false);
GZipStream testStream = new GZipStream(rewindableStream, CompressionMode.Decompress);
if (TarArchive.IsTarFile(testStream))
{
rewindableStream.Rewind(true);
return new TarReader(rewindableStream, CompressionType.GZip, options);
}
rewindableStream.Rewind(true);
return GZipReader.Open(rewindableStream, options);
}
#endif
#if BZIP2
rewindableStream.Rewind(false);
if (BZip2Stream.IsBZip2(rewindableStream))
{
rewindableStream.Rewind(false);
BZip2Stream testStream = new BZip2Stream(rewindableStream, CompressionMode.Decompress, false);
#if TAR
if (TarArchive.IsTarFile(testStream))
{
rewindableStream.Rewind(true);
return new TarReader(rewindableStream, CompressionType.BZip2, options);
}
#endif
}
#endif
#if TAR
rewindableStream.Rewind(false);
if (TarArchive.IsTarFile(rewindableStream))
{
rewindableStream.Rewind(true);
return TarReader.Open(rewindableStream, options);
}
#endif
#if RAR
rewindableStream.Rewind(false);
if (RarArchive.IsRarFile(rewindableStream, options))
{
rewindableStream.Rewind(true);
return RarReader.Open(rewindableStream, options);
}
#endif
throw new InvalidOperationException("Cannot determine compressed stream type. Supported Reader Formats: Zip, GZip, BZip2, Tar, Rar");
}
}
}

View File

@@ -0,0 +1,67 @@
using System.Collections.Generic;
using System.IO;
using SharpCompress.Common;
using SharpCompress.Common.Zip;
using SharpCompress.Common.Zip.Headers;
namespace SharpCompress.Reader.Zip
{
internal class ZipReader : AbstractReader<ZipEntry, ZipVolume>
{
private readonly StreamingZipHeaderFactory headerFactory;
private readonly ZipVolume volume;
internal ZipReader(Stream stream, Options options, string password)
: base(options, ArchiveType.Zip)
{
volume = new ZipVolume(stream, options);
headerFactory = new StreamingZipHeaderFactory(password);
}
public override ZipVolume Volume
{
get { return volume; }
}
#region Open
/// <summary>
/// Opens a ZipReader for Non-seeking usage with a single volume
/// </summary>
/// <param name="stream"></param>
/// <param name="options"></param>
/// <param name="password"></param>
/// <returns></returns>
public static ZipReader Open(Stream stream, string password = null,
Options options = Options.KeepStreamsOpen)
{
stream.CheckNotNull("stream");
return new ZipReader(stream, options, password);
}
#endregion
internal override IEnumerable<ZipEntry> GetEntries(Stream stream)
{
foreach (ZipHeader h in headerFactory.ReadStreamHeader(stream))
{
if (h != null)
{
switch (h.ZipHeaderType)
{
case ZipHeaderType.LocalEntry:
{
yield return new ZipEntry(new StreamingZipFilePart(h as LocalEntryHeader,
stream));
}
break;
case ZipHeaderType.DirectoryEnd:
{
yield break;
}
}
}
}
}
}
}