Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

431 lines
13 KiB
C#

// SimpleZip.cs
//
// Copyright 2005 John Reilly
//
// 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.Core;
namespace ICSharpCode.SharpZipLib.Zip
{
/// <summary>
/// FastZipEvents supports all events applicable to <see cref="FastZip">FastZip</see> operations.
/// </summary>
public class FastZipEvents
{
/// <summary>
/// Delegate to invoke when processing directories.
/// </summary>
public ProcessDirectoryDelegate ProcessDirectory;
/// <summary>
/// Delegate to invoke when processing files.
/// </summary>
public ProcessFileDelegate ProcessFile;
/// <summary>
/// Delegate to invoke when processing directory failures.
/// </summary>
public DirectoryFailureDelegate DirectoryFailure;
/// <summary>
/// Delegate to invoke when processing file failures.
/// </summary>
public FileFailureDelegate FileFailure;
/// <summary>
/// Raise the directory failure event.
/// </summary>
/// <param name="directory">The directory.</param>
/// <param name="e">The exception for this event.</param>
public void OnDirectoryFailure(string directory, Exception e)
{
if ( DirectoryFailure != null ) {
ScanFailureEventArgs args = new ScanFailureEventArgs(directory, e);
DirectoryFailure(this, args);
}
}
/// <summary>
/// Raises the file failure event.
/// </summary>
/// <param name="file">The file for this event.</param>
/// <param name="e">The exception for this event.</param>
public void OnFileFailure(string file, Exception e)
{
if ( FileFailure != null ) {
ScanFailureEventArgs args = new ScanFailureEventArgs(file, e);
FileFailure(this, args);
}
}
/// <summary>
/// Raises the ProcessFileEvent.
/// </summary>
/// <param name="file">The file for this event.</param>
public void OnProcessFile(string file)
{
if ( ProcessFile != null ) {
ScanEventArgs args = new ScanEventArgs(file);
ProcessFile(this, args);
}
}
/// <summary>
/// Raises the ProcessDirectoryEvent.
/// </summary>
/// <param name="directory">The directory for this event.</param>
/// <param name="hasMatchingFiles">Flag indicating if directory has matching files as determined by the current filter.</param>
public void OnProcessDirectory(string directory, bool hasMatchingFiles)
{
if ( ProcessDirectory != null ) {
DirectoryEventArgs args = new DirectoryEventArgs(directory, hasMatchingFiles);
ProcessDirectory(this, args);
}
}
}
/// <summary>
/// FastZip provides facilities for creating and extracting zip files.
/// Only relative paths are supported.
/// </summary>
public class FastZip
{
/// <summary>
/// Initialize a default instance of FastZip.
/// </summary>
public FastZip()
{
this.events = null;
}
/// <summary>
/// Initialise a new instance of <see cref="FastZip"/>
/// </summary>
/// <param name="events"></param>
public FastZip(FastZipEvents events)
{
this.events = events;
}
/// <summary>
/// Defines the desired handling when overwriting files.
/// </summary>
public enum Overwrite {
/// <summary>
/// Prompt the user to confirm overwriting
/// </summary>
Prompt,
/// <summary>
/// Never overwrite files.
/// </summary>
Never,
/// <summary>
/// Always overwrite files.
/// </summary>
Always
}
/// <summary>
/// Get/set a value indicating wether empty directories should be created.
/// </summary>
public bool CreateEmptyDirectories
{
get { return createEmptyDirectories; }
set { createEmptyDirectories = value; }
}
/// <summary>
/// Delegate called when confirming overwriting of files.
/// </summary>
public delegate bool ConfirmOverwriteDelegate(string fileName);
/// <summary>
/// Create a zip file.
/// </summary>
/// <param name="zipFileName">The name of the zip file to create.</param>
/// <param name="sourceDirectory">The directory to source files from.</param>
/// <param name="recurse">True to recurse directories, false for no recursion.</param>
/// <param name="fileFilter">The file filter to apply.</param>
/// <param name="directoryFilter">The directory filter to apply.</param>
public void CreateZip(string zipFileName, string sourceDirectory, bool recurse, string fileFilter, string directoryFilter)
{
NameTransform = new ZipNameTransform(true, sourceDirectory);
this.sourceDirectory = sourceDirectory;
outputStream = new ZipOutputStream(File.Create(zipFileName));
try {
FileSystemScanner scanner = new FileSystemScanner(fileFilter, directoryFilter);
scanner.ProcessFile += new ProcessFileDelegate(ProcessFile);
if ( this.CreateEmptyDirectories ) {
scanner.ProcessDirectory += new ProcessDirectoryDelegate(ProcessDirectory);
}
scanner.Scan(sourceDirectory, recurse);
}
finally {
outputStream.Close();
}
}
/// <summary>
/// Create a zip file/archive.
/// </summary>
/// <param name="zipFileName">The name of the zip file to create.</param>
/// <param name="sourceDirectory">The directory to obtain files and directories from.</param>
/// <param name="recurse">True to recurse directories, false for no recursion.</param>
/// <param name="fileFilter">The file filter to apply.</param>
public void CreateZip(string zipFileName, string sourceDirectory, bool recurse, string fileFilter)
{
CreateZip(zipFileName, sourceDirectory, recurse, fileFilter, null);
}
/// <summary>
/// Extract the contents of a zip file.
/// </summary>
/// <param name="zipFileName">The zip file to extract from.</param>
/// <param name="targetDirectory">The directory to save extracted information in.</param>
/// <param name="fileFilter">A filter to apply to files.</param>
public void ExtractZip(string zipFileName, string targetDirectory, string fileFilter)
{
ExtractZip(zipFileName, targetDirectory, Overwrite.Always, null, fileFilter, null);
}
/// <summary>
/// Exatract the contents of a zip file.
/// </summary>
/// <param name="zipFileName">The zip file to extract from.</param>
/// <param name="targetDirectory">The directory to save extracted information in.</param>
/// <param name="overwrite">The style of <see cref="Overwrite">overwriting</see> to apply.</param>
/// <param name="confirmDelegate">A delegate to invoke when confirming overwriting.</param>
/// <param name="fileFilter">A filter to apply to files.</param>
/// <param name="directoryFilter">A filter to apply to directories.</param>
public void ExtractZip(string zipFileName, string targetDirectory,
Overwrite overwrite, ConfirmOverwriteDelegate confirmDelegate,
string fileFilter, string directoryFilter)
{
if ((overwrite == Overwrite.Prompt) && (confirmDelegate == null)) {
throw new ArgumentNullException("confirmDelegate");
}
this.overwrite = overwrite;
this.confirmDelegate = confirmDelegate;
this.targetDirectory = targetDirectory;
this.fileFilter = new NameFilter(fileFilter);
this.directoryFilter = new NameFilter(directoryFilter);
inputStream = new ZipInputStream(File.OpenRead(zipFileName));
try {
if (password != null) {
inputStream.Password = password;
}
ZipEntry entry;
while ( (entry = inputStream.GetNextEntry()) != null ) {
if ( this.directoryFilter.IsMatch(Path.GetDirectoryName(entry.Name)) && this.fileFilter.IsMatch(entry.Name) ) {
ExtractEntry(entry);
}
}
}
finally {
inputStream.Close();
}
}
void ProcessDirectory(object sender, DirectoryEventArgs e)
{
if ( !e.HasMatchingFiles && createEmptyDirectories ) {
if ( events != null ) {
events.OnProcessDirectory(e.Name, e.HasMatchingFiles);
}
if (e.Name != sourceDirectory) {
string cleanedName = nameTransform.TransformDirectory(e.Name);
ZipEntry entry = new ZipEntry(cleanedName);
outputStream.PutNextEntry(entry);
}
}
}
void ProcessFile(object sender, ScanEventArgs e)
{
if ( events != null ) {
events.OnProcessFile(e.Name);
}
string cleanedName = nameTransform.TransformFile(e.Name);
ZipEntry entry = new ZipEntry(cleanedName);
outputStream.PutNextEntry(entry);
AddFileContents(e.Name);
}
void AddFileContents(string name)
{
if ( buffer == null ) {
buffer = new byte[4096];
}
FileStream stream = File.OpenRead(name);
try {
int length;
do {
length = stream.Read(buffer, 0, buffer.Length);
outputStream.Write(buffer, 0, length);
} while ( length > 0 );
}
finally {
stream.Close();
}
}
void ExtractFileEntry(ZipEntry entry, string targetName)
{
bool proceed = true;
if ((overwrite == Overwrite.Prompt) && (confirmDelegate != null)) {
if (File.Exists(targetName) == true) {
proceed = confirmDelegate(targetName);
}
}
if ( proceed ) {
if ( events != null ) {
events.OnProcessFile(entry.Name);
}
FileStream streamWriter = File.Create(targetName);
try {
if ( buffer == null ) {
buffer = new byte[4096];
}
int size;
do {
size = inputStream.Read(buffer, 0, buffer.Length);
streamWriter.Write(buffer, 0, size);
} while (size > 0);
}
finally {
streamWriter.Close();
}
if (restoreDateTime) {
File.SetLastWriteTime(targetName, entry.DateTime);
}
}
}
bool NameIsValid(string name)
{
return name != null && name.Length > 0 && name.IndexOfAny(Path.InvalidPathChars) < 0;
}
void ExtractEntry(ZipEntry entry)
{
bool doExtraction = NameIsValid(entry.Name);
string dirName = null;
string targetName = null;
if ( doExtraction ) {
string entryFileName;
if (Path.IsPathRooted(entry.Name)) {
string workName = Path.GetPathRoot(entry.Name);
workName = entry.Name.Substring(workName.Length);
entryFileName = Path.Combine(Path.GetDirectoryName(workName), Path.GetFileName(entry.Name));
} else {
entryFileName = entry.Name;
}
targetName = Path.Combine(targetDirectory, entryFileName);
dirName = Path.GetDirectoryName(Path.GetFullPath(targetName));
doExtraction = doExtraction && (entryFileName.Length > 0);
}
if ( doExtraction && !Directory.Exists(dirName) )
{
if ( !entry.IsDirectory || this.CreateEmptyDirectories ) {
try {
Directory.CreateDirectory(dirName);
}
catch {
doExtraction = false;
}
}
}
if ( doExtraction && entry.IsFile ) {
ExtractFileEntry(entry, targetName);
}
}
/// <summary>
/// Get or set the <see cref="ZipNameTransform"> active when creating Zip files.</see>
/// </summary>
public ZipNameTransform NameTransform
{
get { return nameTransform; }
set {
if ( value == null ) {
nameTransform = new ZipNameTransform();
}
else {
nameTransform = value;
}
}
}
#region Instance Fields
byte[] buffer;
ZipOutputStream outputStream;
ZipInputStream inputStream;
string password = null;
string targetDirectory;
string sourceDirectory;
NameFilter fileFilter;
NameFilter directoryFilter;
Overwrite overwrite;
ConfirmOverwriteDelegate confirmDelegate;
bool restoreDateTime = false;
bool createEmptyDirectories = false;
FastZipEvents events;
ZipNameTransform nameTransform;
#endregion
}
}