// SharpZipLibrary samples
//
// $Id: sz.cs 488 2009-10-06 18:13:09Z johnreilly $
// $URL: svn://svn.sharpdevelop.net/sharpziplib/trunk/SharpZipLib/samples/cs/sz/sz.cs $
//
// Copyright (c) 2007, AlphaSierraPapa
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// - Redistributions of source code must retain the above copyright notice, this list
// of conditions and the following disclaimer.
//
// - Redistributions in binary form must reproduce the above copyright notice, this list
// of conditions and the following disclaimer in the documentation and/or other materials
// provided with the distribution.
//
// - Neither the name of the SharpDevelop team nor the names of its contributors may be used to
// endorse or promote products derived from this software without specific prior written
// permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &AS IS& AND ANY EXPRESS
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Define test to add more detailed Console.WriteLine style information
// #define TEST
using System;
using System.IO;
using System.Collections;
using System.Text;
using System.Globalization;
using System.Reflection;
using ICSharpCode.SharpZipLib.Core;
using ICSharpCode.SharpZipLib.Zip;
using ICSharpCode.SharpZipLib.Zip.Compression;
namespace ICSharpCode.SharpZipLib.Samples.SZ
{
///
/// A command line archiver using the SharpZipLib compression library
///
public class SharpZipArchiver {
///
/// Options for handling overwriting of files.
///
enum Overwrite {
Prompt,
Never,
Always
}
///
/// The operations that can be performed.
///
enum Operation
{
Create, // add files to new archive
Extract, // extract files from existing archive
List, // show contents of existing archive
Delete, // Delete from archive
Add, // Add to archive.
Test, // Test the archive for validity.
}
///
/// Interpret attributes based on the operating system they are from.
///
/// The operating system to base interpretation of attributes on.
/// The external attributes.
/// A string representation of the attributres passed.
static string InterpretExternalAttributes(int operatingSystem, int attributes)
{
string result = string.Empty;
if ((operatingSystem == 0) || (operatingSystem == 10)) {
// Directory
if ((attributes & 0x10) != 0)
result = result + "D";
else
result = result + "-";
// Volume
if ((attributes & 0x08) != 0)
result = result + "V";
else
result = result + "-";
// Read-only
if ((attributes & 0x01) != 0)
result = result + "r";
else
result = result + "-";
// Archive
if ((attributes & 0x20) != 0)
result = result + "a";
else
result = result + "-";
// System
if ((attributes & 0x04) != 0)
result = result + "s";
else
result = result + "-";
// Hidden
if ((attributes & 0x02) != 0)
result = result + "h";
else
result = result + "-";
// Device
if ((attributes & 0x4) != 0)
result = result + "d";
else
result = result + "-";
// OS is NTFS
if ( operatingSystem == 10 )
{
// Encrypted
if ( (attributes & 0x4000) != 0 ) {
result += "E";
}
else {
result += "-";
}
// Not content indexed
if ( (attributes & 0x2000) != 0 ) {
result += "n";
}
else {
result += "-";
}
// Offline
if ( (attributes & 0x1000) != 0 ) {
result += "O";
}
else {
result += "-";
}
// Compressed
if ( (attributes & 0x0800) != 0 ) {
result += "C";
}
else {
result += "-";
}
// Reparse point
if ( (attributes & 0x0400) != 0 ) {
result += "R";
}
else {
result += "-";
}
// Sparse
if ( (attributes & 0x0200) != 0 ) {
result += "S";
}
else {
result += "-";
}
// Temporary
if ( (attributes & 0x0100) != 0 ) {
result += "T";
}
else {
result += "-";
}
}
}
return result;
}
///
/// Determine if string is numeric [0-9]+
///
/// string to test
/// true iff rhs is numeric
static bool IsNumeric(string rhs)
{
bool result;
if ((rhs != null) && (rhs.Length > 0)) {
result = true;
for (int i = 0; result && (i < rhs.Length); ++i) {
if (!char.IsDigit(rhs[i])) {
result = false;
}
}
} else {
result = false;
}
return result;
}
///
/// Parse command line arguments.
/// This is fairly flexible without using any custom classes. Arguments and options can appear
/// in any order and are case insensitive. Arguments for options are indicated with an '='
/// as in -demo=argument, sometimes the '=' can be omitted as well secretly.
/// Grouping of single character options is supported.
///
/// The actual arguments and their handling is however a grab bag of ad-hoc things and its a bit messy. Could be a
/// bit more rigorous about how things are done. Up side is almost anything is/can be allowed
///
///
/// true if arguments are valid such that processing should continue
///
bool SetArgs(string[] args) {
bool result = true;
int argIndex = 0;
while (argIndex < args.Length) {
if (args[argIndex][0] == '-' || args[argIndex][0] == '/') {
string option = args[argIndex].Substring(1).ToLower();
string optArg = "";
int parameterIndex = option.IndexOf('=');
if (parameterIndex >= 0) {
if (parameterIndex < option.Length - 1) {
optArg = option.Substring(parameterIndex + 1);
}
option = option.Substring(0, parameterIndex);
}
if (option.Length == 0) {
Console.WriteLine("Invalid argument {0}", args[argIndex]);
result = false;
}
else {
int optionIndex = 0;
while (optionIndex < option.Length) {
switch(option[optionIndex]) {
case '-': // long option
optionIndex = option.Length;
switch (option) {
case "-abs":
relativePathInfo = false;
break;
case "-add":
operation = Operation.Add;
break;
case "-create":
operation = Operation.Create;
break;
case "-list":
operation = Operation.List;
useZipFileWhenListing = true;
break;
case "-extract":
operation = Operation.Extract;
if (optArg.Length > 0) {
targetOutputDirectory = optArg;
}
break;
case "-delete":
operation = Operation.Delete;
break;
case "-test":
operation = Operation.Test;
break;
case "-info":
ShowEnvironment();
break;
case "-emptydirs":
addEmptyDirectoryEntries = true;
break;
case "-data":
testData = true;
break;
case "-extractdir":
if (optArg.Length > 0) {
targetOutputDirectory = optArg;
} else {
result = false;
Console.WriteLine("Invalid extractdir " + args[argIndex]);
}
break;
case "-zip64":
if ( optArg.Length > 0 ) {
switch ( optArg ) {
case "on":
useZip64_ = UseZip64.On;
break;
case "off":
useZip64_ = UseZip64.Off;
break;
case "auto":
useZip64_ = UseZip64.Dynamic;
break;
}
}
break;
case "-encoding":
if (optArg.Length > 0) {
if (IsNumeric(optArg)) {
try {
int enc = int.Parse(optArg);
if (Encoding.GetEncoding(enc) != null) {
ZipConstants.DefaultCodePage = enc;
} else {
result = false;
Console.WriteLine("Invalid encoding " + args[argIndex]);
}
}
catch (Exception) {
result = false;
Console.WriteLine("Invalid encoding " + args[argIndex]);
}
} else {
try {
ZipConstants.DefaultCodePage = Encoding.GetEncoding(optArg).CodePage;
}
catch (Exception) {
result = false;
Console.WriteLine("Invalid encoding " + args[argIndex]);
}
}
} else {
result = false;
Console.WriteLine("Missing encoding parameter");
}
break;
case "-store":
useZipStored = true;
break;
case "-deflate":
useZipStored = false;
break;
case "-version":
ShowVersion();
break;
case "-help":
ShowHelp();
break;
#if !NETCF
case "-restore-dates":
restoreDateTime = true;
break;
#endif
default:
Console.WriteLine("Invalid long argument " + args[argIndex]);
result = false;
break;
}
break;
case '?':
ShowHelp();
break;
case 's':
if (optionIndex != 0) {
result = false;
Console.WriteLine("-s cannot be in a group");
} else {
if (optArg.Length > 0) {
password = optArg;
} else if (option.Length > 1) {
password = option.Substring(1);
} else {
Console.WriteLine("Missing argument to " + args[argIndex]);
}
}
optionIndex = option.Length;
break;
case 'c':
operation = Operation.Create;
break;
case 'l':
if (optionIndex != 0) {
result = false;
Console.WriteLine("-l cannot be in a group");
} else {
if (optArg.Length > 0) {
try {
compressionLevel = int.Parse(optArg);
}
catch (Exception) {
Console.WriteLine("Level invalid");
}
}
}
optionIndex = option.Length;
break;
case 'o':
optionIndex += 1;
overwriteFiles = (optionIndex < option.Length) ? (option[optionIndex] == '+') ? Overwrite.Always : Overwrite.Never : Overwrite.Never;
break;
case 'p':
relativePathInfo = true;
break;
case 'q':
silent = true;
if (overwriteFiles == Overwrite.Prompt) {
overwriteFiles = Overwrite.Never;
}
break;
case 'r':
recursive = true;
break;
case 'v':
operation = Operation.List;
break;
case 'x':
if (optionIndex != 0) {
result = false;
Console.WriteLine("-x cannot be in a group");
} else {
operation = Operation.Extract;
if (optArg.Length > 0) {
targetOutputDirectory = optArg;
}
}
optionIndex = option.Length;
break;
default:
Console.WriteLine("Invalid argument: " + args[argIndex]);
result = false;
break;
}
++optionIndex;
}
}
}
else {
fileSpecs.Add(args[argIndex]);
}
++argIndex;
}
if (fileSpecs.Count > 0 && operation == Operation.Create) {
string checkPath = (string)fileSpecs[0];
int deviceCheck = checkPath.IndexOf(':');
#if NETCF_1_0
if (checkPath.IndexOfAny(Path.InvalidPathChars) >= 0
#else
if (checkPath.IndexOfAny(Path.GetInvalidPathChars()) >= 0
#endif
|| checkPath.IndexOf('*') >= 0 || checkPath.IndexOf('?') >= 0
|| (deviceCheck >= 0 && deviceCheck != 1)) {
Console.WriteLine("There are invalid characters in the specified zip file name");
result = false;
}
}
return result && (fileSpecs.Count > 0);
}
///
/// Show encoding/locale information
///
void ShowEnvironment()
{
seenHelp = true;
#if !NETCF_1_0
Console.WriteLine(
"Current encoding is {0}, code page {1}, windows code page {2}",
Console.Out.Encoding.EncodingName,
Console.Out.Encoding.CodePage,
Console.Out.Encoding.WindowsCodePage);
Console.WriteLine("Default code page is {0}",
Encoding.Default.CodePage);
Console.WriteLine( "Current culture LCID 0x{0:X}, {1}", CultureInfo.CurrentCulture.LCID, CultureInfo.CurrentCulture.EnglishName);
Console.WriteLine( "Current thread OEM codepage {0}", System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo.OEMCodePage);
Console.WriteLine( "Current thread Mac codepage {0}", System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo.MacCodePage);
Console.WriteLine( "Current thread Ansi codepage {0}", System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ANSICodePage);
#endif
}
///
/// Display version information
///
void ShowVersion() {
seenHelp = true;
Console.WriteLine("SharpZip Archiver v0.37 Copyright 2004 John Reilly");
#if !NETCF
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies) {
if (assembly.GetName().Name == "ICSharpCode.SharpZipLib") {
Console.WriteLine("#ZipLib v{0} {1}", assembly.GetName().Version,
assembly.GlobalAssemblyCache ? "Running from GAC" : "Running from DLL"
);
}
}
#endif
}
///
/// Show help on possible options and arguments
///
void ShowHelp()
{
if (seenHelp) {
return;
}
seenHelp = true;
ShowVersion();
Console.WriteLine("usage sz {options} archive files");
Console.WriteLine("");
Console.WriteLine("Options:");
Console.WriteLine("-abs Store absolute path info");
Console.WriteLine("-?, --help Show this help");
Console.WriteLine("-c --create Create new archive");
Console.WriteLine("-v List archive contents (default)");
Console.WriteLine("--list List archive contents extended format");
Console.WriteLine("-x{=dir}, --extract{=dir} Extract archive contents to dir(default .)");
Console.WriteLine("--extractdir=path Set extract directory (default .)");
Console.WriteLine("--info Show current environment information" );
Console.WriteLine("--store Store entries (default=deflate)");
Console.WriteLine("--version Show version information");
Console.WriteLine("--emptydirs Create entries for empty directories");
Console.WriteLine("--encoding=codepage|name Set code page for encoding by name or number");
Console.WriteLine("--zip64=on|off|auto Set use zip64 flag (default is auto)");
#if !NETCF
Console.WriteLine("--restore-dates Restore dates on extraction");
#endif
Console.WriteLine("--delete Delete files from archive");
Console.WriteLine("--test Test archive for validity");
Console.WriteLine("--data Test archive data");
Console.WriteLine("--add Add files to archive");
Console.WriteLine("-o+ Overwrite files without prompting");
Console.WriteLine("-o- Never overwrite files");
Console.WriteLine("-p Store relative path info (default)");
Console.WriteLine("-r Recurse sub-folders");
Console.WriteLine("-q Quiet mode");
Console.WriteLine("-s=password Set archive password");
Console.WriteLine("-l=level Use compression level (0-9) when compressing");
Console.WriteLine("");
}
///
/// Calculate compression ratio as a percentage
/// Doesnt allow for expansion (ratio > 100) as the resulting strings can get huge easily
///
int GetCompressionRatio(long packedSize, long unpackedSize)
{
int result = 0;
if (unpackedSize > 0 && unpackedSize >= packedSize) {
result = (int) Math.Round((1.0 - ((double)packedSize / (double)unpackedSize)) * 100.0);
}
return result;
}
///
/// List zip file contents using stream
///
/// File to list contents of
void ListZip(string fileName) {
try {
// TODO for asian/non-latin/non-proportional fonts string lengths dont work so output may not line up
const string headerTitles = "Name Length Ratio Size Date & time CRC-32";
const string headerUnderline = "--------------- ---------- ----- ---------- ------------------- --------";
FileInfo fileInfo = new FileInfo(fileName);
if (fileInfo.Exists == false) {
Console.WriteLine("No such file exists {0}", fileName);
return;
}
Console.WriteLine(fileName);
using (FileStream fileStream = File.OpenRead(fileName)) {
using (ZipInputStream stream = new ZipInputStream(fileStream)) {
if ((password != null) && (password.Length > 0)) {
stream.Password = password;
}
int entryCount = 0;
long totalSize = 0;
ZipEntry theEntry;
while ((theEntry = stream.GetNextEntry()) != null) {
if ( theEntry.IsDirectory ) {
Console.WriteLine("Directory {0}", theEntry.Name);
continue;
}
if ( !theEntry.IsFile ) {
Console.WriteLine("Non file entry {0}", theEntry.Name);
continue;
}
if (entryCount == 0) {
Console.WriteLine(headerTitles);
Console.WriteLine(headerUnderline);
}
++entryCount;
int ratio = GetCompressionRatio(theEntry.CompressedSize, theEntry.Size);
totalSize += theEntry.Size;
if (theEntry.Name.Length > 15) {
Console.WriteLine(theEntry.Name);
Console.WriteLine(
"{0,-15} {1,10:0} {2,3}% {3,10:0} {4,10:d} {4:hh:mm:ss} {5,8:x}",
"", theEntry.Size, ratio, theEntry.CompressedSize, theEntry.DateTime, theEntry.Crc);
} else {
Console.WriteLine(
"{0,-15} {1,10:0} {2,3}% {3,10:0} {4,10:d} {4:hh:mm:ss} {5,8:x}",
theEntry.Name, theEntry.Size, ratio, theEntry.CompressedSize, theEntry.DateTime, theEntry.Crc);
}
}
if (entryCount == 0) {
Console.WriteLine("Archive is empty!");
} else {
Console.WriteLine(headerUnderline);
Console.WriteLine(
"{0,-15} {1,10:0} {2,3}% {3,10:0} {4,10:d} {4:hh:mm:ss}",
entryCount.ToString() + " entries", totalSize, GetCompressionRatio(fileInfo.Length, totalSize), fileInfo.Length, fileInfo.LastWriteTime);
}
}
}
}
catch(Exception exception)
{
Console.WriteLine("Exception during list operation: {0}", exception.Message);
}
}
///
/// List zip file contents using class
///
/// File to list contents of
void ListZipViaZipFile(string fileName) {
try {
const string headerTitles = "Name Length Ratio Size Date & time CRC-32 Attr";
const string headerUnderline = "------------ ---------- ----- ---------- ------------------- -------- ------";
FileInfo fileInfo = new FileInfo(fileName);
if (fileInfo.Exists == false) {
Console.WriteLine("No such file exists {0}", fileName);
return;
}
Console.WriteLine(fileName);
int entryCount = 0;
long totalSize = 0;
using (ZipFile zipFile = new ZipFile(fileName)) {
foreach (ZipEntry theEntry in zipFile) {
if ( theEntry.IsDirectory ) {
Console.WriteLine("Directory {0}", theEntry.Name);
}
else if ( !theEntry.IsFile ) {
Console.WriteLine("Non file entry {0}", theEntry.Name);
continue;
}
else {
if (entryCount == 0) {
Console.WriteLine(headerTitles);
Console.WriteLine(headerUnderline);
}
++entryCount;
int ratio = GetCompressionRatio(theEntry.CompressedSize, theEntry.Size);
totalSize += theEntry.Size;
if (theEntry.Name.Length > 12) {
Console.WriteLine(theEntry.Name);
Console.WriteLine(
"{0,-12} {1,10:0} {2,3}% {3,10:0} {4,10:d} {4:hh:mm:ss} {5,8:x} {6,4}",
"", theEntry.Size, ratio, theEntry.CompressedSize, theEntry.DateTime, theEntry.Crc,
InterpretExternalAttributes(theEntry.HostSystem, theEntry.ExternalFileAttributes));
}
else {
Console.WriteLine(
"{0,-12} {1,10:0} {2,3}% {3,10:0} {4,10:d} {4:hh:mm:ss} {5,8:x} {6,4}",
theEntry.Name, theEntry.Size, ratio, theEntry.CompressedSize, theEntry.DateTime, theEntry.Crc,
InterpretExternalAttributes(theEntry.HostSystem, theEntry.ExternalFileAttributes));
}
}
}
}
if (entryCount == 0) {
Console.WriteLine("Archive is empty!");
} else {
Console.WriteLine(headerUnderline);
Console.WriteLine(
"{0,-12} {1,10:0} {2,3}% {3,10:0} {4,10:d} {4:hh:mm:ss}",
entryCount.ToString() + " entries", totalSize, GetCompressionRatio(fileInfo.Length, totalSize), fileInfo.Length, fileInfo.LastWriteTime);
}
}
catch(Exception exception) {
Console.WriteLine("Exception during list operation: {0}", exception.Message);
}
}
///
/// Execute List operation
/// Currently only Zip files are supported
///
/// Files to list
void List(ArrayList fileSpecifications)
{
foreach (string spec in fileSpecifications) {
string [] names;
string pathName = Path.GetDirectoryName(spec);
if ( (pathName == null) || (pathName.Length == 0) ) {
pathName = @".\";
}
names = Directory.GetFiles(pathName, Path.GetFileName(spec));
if (names.Length == 0) {
Console.WriteLine("No files found matching {0}", spec);
}
else {
foreach (string file in names) {
if (useZipFileWhenListing) {
ListZipViaZipFile(file);
} else {
ListZip(file);
}
Console.WriteLine("");
}
}
}
}
///
/// 'Cook' a name making it acceptable as a zip entry name.
///
/// name to cook
/// String to remove from front of name if present
/// Make names relative if true or absolute if false
static public string CookZipEntryName(string name, string stripPrefix, bool relativePath)
{
#if TEST
Console.WriteLine("Cooking '{0}' prefix is '{1}'", name, stripPrefix);
#endif
if (name == null) {
return "";
}
if (stripPrefix != null && stripPrefix.Length > 0 && name.IndexOf(stripPrefix, 0) == 0) {
name = name.Substring(stripPrefix.Length);
}
if (Path.IsPathRooted(name)) {
// NOTE:
// for UNC names... \\machine\share\zoom\beet.txt gives \zoom\beet.txt
name = name.Substring(Path.GetPathRoot(name).Length);
#if TEST
Console.WriteLine("Removing root info {0}", name);
#endif
}
name = name.Replace(@"\", "/");
if (relativePath) {
if (name.Length > 0 && (name[0] == Path.AltDirectorySeparatorChar || name[0] == Path.DirectorySeparatorChar)) {
name = name.Remove(0, 1);
}
} else {
if (name.Length > 0 && name[0] != Path.AltDirectorySeparatorChar && name[0] != Path.DirectorySeparatorChar) {
name = name.Insert(0, "/");
}
}
#if TEST
Console.WriteLine("Cooked value '{0}'", name);
#endif
return name;
}
///
/// Make string into something acceptable as an entry name
///
/// Name to 'cook'
string CookZipEntryName(string name)
{
return CookZipEntryName(name, removablePathPrefix, relativePathInfo);
}
// TODO: Add equivalent for non-seekable output
///
/// Add a file were the output is seekable
///
void AddFileSeekableOutput(string file, string entryPath)
{
ZipEntry entry = new ZipEntry(entryPath);
FileInfo fileInfo = new FileInfo(file);
entry.DateTime = fileInfo.LastWriteTime; // or DateTime.Now or whatever, for now use the file
entry.ExternalFileAttributes = (int)fileInfo.Attributes;
entry.Size = fileInfo.Length;
if (useZipStored) {
entry.CompressionMethod = CompressionMethod.Stored;
} else {
entry.CompressionMethod = CompressionMethod.Deflated;
}
using (FileStream fileStream = File.OpenRead(file)) {
outputStream.PutNextEntry(entry);
StreamUtils.Copy(fileStream, outputStream, GetBuffer());
}
}
byte[] GetBuffer()
{
if ( buffer == null ) {
buffer = new byte[bufferSize_];
}
return buffer;
}
///
/// Add file to archive
///
/// file to add
void AddFile(string fileName) {
#if TEST
Console.WriteLine("AddFile {0}", fileName);
#endif
if (File.Exists(fileName)) {
string entryName = CookZipEntryName(fileName);
if (silent == false) {
Console.Write(" " + entryName);
}
AddFileSeekableOutput(fileName, entryName);
if (silent == false) {
Console.WriteLine("");
}
} else {
Console.WriteLine("No such file exists {0}", fileName);
}
}
///
/// Add an entry for a folder or directory
///
/// The name of the folder to add
void AddFolder(string folderName)
{
#if TEST
Console.WriteLine("AddFolder {0}", folderName);
#endif
folderName = CookZipEntryName(folderName);
if (folderName.Length == 0 || folderName[folderName.Length - 1] != '/') {
folderName = folderName + '/';
}
ZipEntry zipEntry = new ZipEntry(folderName);
outputStream.PutNextEntry(zipEntry);
}
///
/// Compress contents of folder
///
/// The folder to compress
/// If true process recursively
/// Pattern to match for files
/// Number of entries added
int CompressFolder(string basePath, bool recursiveSearch, string searchPattern)
{
int result = 0;
#if TEST
Console.WriteLine("CompressFolder basepath {0} pattern {1}", basePath, searchPattern);
#endif
string [] names = Directory.GetFiles(basePath, searchPattern);
foreach (string fileName in names) {
AddFile(fileName);
++result;
}
if (names.Length == 0 && addEmptyDirectoryEntries) {
AddFolder(basePath);
++result;
}
if (recursiveSearch) {
names = Directory.GetDirectories(basePath);
foreach (string folderName in names) {
result += CompressFolder(folderName, true, searchPattern);
}
}
return result;
}
///
/// Create archives based on specifications passed and internal state
///
void Create(ArrayList fileSpecifications)
{
string zipFileName = fileSpecifications[0] as string;
if (Path.GetExtension(zipFileName).Length == 0) {
zipFileName = Path.ChangeExtension(zipFileName, ".zip");
}
fileSpecifications.RemoveAt(0);
if (overwriteFiles == Overwrite.Never && File.Exists(zipFileName)) {
Console.WriteLine("File {0} already exists", zipFileName);
return;
}
int totalEntries = 0;
using (FileStream stream = File.Create(zipFileName)) {
using (outputStream = new ZipOutputStream(stream)) {
if ((password != null) && (password.Length > 0)) {
outputStream.Password = password;
}
outputStream.UseZip64 = useZip64_;
outputStream.SetLevel(compressionLevel);
foreach(string spec in fileSpecifications) {
string fileName = Path.GetFileName(spec);
string pathName = Path.GetDirectoryName(spec);
if (pathName == null || pathName.Length == 0) {
pathName = Path.GetFullPath(".");
if (relativePathInfo == true) {
removablePathPrefix = pathName;
}
} else {
pathName = Path.GetFullPath(pathName);
// TODO: for paths like ./txt/*.txt the prefix should be fullpath for .
// for z:txt/*.txt should be fullpath for z:.
if (relativePathInfo == true) {
removablePathPrefix = pathName;
}
}
// TODO wildcards arent full supported by this
if (recursive || fileName.IndexOf('*') >= 0 || fileName.IndexOf('?') >= 0) {
// TODO this allows possible conflicts in filenames that are added to Zip file
// as part of different file specs.
totalEntries += CompressFolder(pathName, recursive, fileName);
} else {
AddFile(pathName + @"\" + fileName);
++totalEntries;
}
}
if (totalEntries == 0) {
Console.WriteLine("File created has no entries!");
}
}
}
}
bool ExtractFile(ZipInputStream inputStream, ZipEntry theEntry, string targetDir)
{
// try and sort out the correct place to save this entry
string entryFileName;
if (Path.IsPathRooted(theEntry.Name)) {
string workName = Path.GetPathRoot(theEntry.Name);
workName = theEntry.Name.Substring(workName.Length);
entryFileName = Path.Combine(Path.GetDirectoryName(workName), Path.GetFileName(theEntry.Name));
}
else {
entryFileName = theEntry.Name;
}
string targetName = Path.Combine(targetDir, entryFileName);
string fullPath = Path.GetDirectoryName(Path.GetFullPath(targetName));
#if TEST
Console.WriteLine("Decompress targetfile name " + entryFileName);
Console.WriteLine("Decompress targetpath " + fullPath);
#endif
// Could be an option or parameter to allow failure or try creation
if (Directory.Exists(fullPath) == false) {
try {
Directory.CreateDirectory(fullPath);
}
catch {
return false;
}
}
else if (overwriteFiles == Overwrite.Prompt) {
if (File.Exists(targetName) == true) {
Console.Write("File " + targetName + " already exists. Overwrite? ");
// TODO sort out the complexities of Read so single key presses can be used
string readValue;
try {
readValue = Console.ReadLine();
}
catch {
readValue = null;
}
if (readValue == null || readValue.ToLower() != "y") {
#if TEST
Console.WriteLine("Skipped!");
#endif
return true;
}
}
}
if (entryFileName.Length > 0) {
#if TEST
Console.WriteLine("Extracting...");
#endif
using (FileStream streamWriter = File.Create(targetName)) {
byte[] data = new byte[4096];
int size;
do {
size = inputStream.Read(data, 0, data.Length);
streamWriter.Write(data, 0, size);
} while (size > 0);
}
#if !NETCF
if (restoreDateTime) {
File.SetLastWriteTime(targetName, theEntry.DateTime);
}
#endif
}
return true;
}
void ExtractDirectory(ZipInputStream inputStream, ZipEntry theEntry, string targetDir)
{
// For now do nothing.
}
///
/// Decompress a file
///
/// File to decompress
/// Directory to create output in
/// true iff all has been done successfully
bool DecompressFile(string fileName, string targetDir)
{
bool result = true;
try {
using (ZipInputStream inputStream = new ZipInputStream(File.OpenRead(fileName))) {
if (password != null) {
inputStream.Password = password;
}
ZipEntry theEntry;
while ((theEntry = inputStream.GetNextEntry()) != null) {
if ( theEntry.IsFile ) {
ExtractFile(inputStream, theEntry, targetDir);
}
else if ( theEntry.IsDirectory )
{
ExtractDirectory(inputStream, theEntry, targetDir);
}
}
}
}
catch (Exception except) {
result = false;
Console.WriteLine(except.Message + " Failed to unzip file");
}
return result;
}
///
/// Extract archives based on user input
/// Allows simple wildcards to specify multiple archives
///
void Extract(ArrayList fileSpecs)
{
if (targetOutputDirectory == null || targetOutputDirectory.Length == 0) {
targetOutputDirectory = @".\";
}
foreach(string spec in fileSpecs) {
string [] names;
if (spec.IndexOf('*') >= 0 || spec.IndexOf('?') >= 0) {
string pathName = Path.GetDirectoryName(spec);
if (pathName == null || pathName.Length == 0) {
pathName = @".\";
}
names = Directory.GetFiles(pathName, Path.GetFileName(spec));
} else {
names = new string[] { spec };
}
foreach (string fileName in names) {
if (File.Exists(fileName) == false) {
Console.WriteLine("No such file exists {0}", spec);
} else {
DecompressFile(fileName, targetOutputDirectory);
}
}
}
}
void Test(ArrayList fileSpecs)
{
string zipFileName = fileSpecs[0] as string;
if (Path.GetExtension(zipFileName).Length == 0) {
zipFileName = Path.ChangeExtension(zipFileName, ".zip");
}
using (ZipFile zipFile = new ZipFile(zipFileName)) {
if ( zipFile.TestArchive(testData) ) {
Console.WriteLine("Archive test passed");
} else {
Console.WriteLine("Archive test failure");
}
}
}
///
/// Delete entries from an archive
///
/// The file specs to operate on.
void Delete(ArrayList fileSpecs)
{
string zipFileName = fileSpecs[0] as string;
if (Path.GetExtension(zipFileName).Length == 0) {
zipFileName = Path.ChangeExtension(zipFileName, ".zip");
}
using (ZipFile zipFile = new ZipFile(zipFileName)) {
zipFile.BeginUpdate();
for ( int i = 1; i < fileSpecs.Count; ++i ) {
zipFile.Delete((string)fileSpecs[i]);
}
zipFile.CommitUpdate();
}
}
void Add(ArrayList fileSpecs)
{
string zipFileName = fileSpecs[0] as string;
if (Path.GetExtension(zipFileName).Length == 0) {
zipFileName = Path.ChangeExtension(zipFileName, ".zip");
}
using (ZipFile zipFile = new ZipFile(zipFileName)) {
zipFile.BeginUpdate();
for ( int i = 1; i < fileSpecs.Count; ++i ) {
zipFile.Add((string)fileSpecs[i]);
}
zipFile.CommitUpdate();
}
}
///
/// Parse command line arguments and 'execute' them.
///
void Execute(string[] args) {
if (SetArgs(args)) {
if (fileSpecs.Count == 0) {
if (!silent) {
Console.WriteLine("Nothing to do");
}
}
else {
switch (operation) {
case Operation.List:
List(fileSpecs);
break;
case Operation.Create:
Create(fileSpecs);
break;
case Operation.Extract:
Extract(fileSpecs);
break;
case Operation.Delete:
Delete(fileSpecs);
break;
case Operation.Add:
Add(fileSpecs);
break;
case Operation.Test:
Test(fileSpecs);
break;
}
}
} else {
if (!silent) {
ShowHelp();
}
}
}
///
/// Entry point for program, creates archiver and runs it
///
///
/// Command line argument to process
///
public static void Main(string[] args) {
SharpZipArchiver sza = new SharpZipArchiver();
sza.Execute(args);
}
#region Instance Fields
///
/// The Zip64 extension use to apply.
///
UseZip64 useZip64_ = UseZip64.Off;
///
/// Has user already seen help output?
///
bool seenHelp;
///
/// The size of the buffer to use when copying.
///
int bufferSize_ = 8192;
///
/// Buffer for use when copying between streams.
///
byte[] buffer;
///
/// File specification possibly with wildcards from command line
///
ArrayList fileSpecs = new ArrayList();
///
/// Deflate compression level
///
int compressionLevel = Deflater.DEFAULT_COMPRESSION;
///
/// Create entries for directories with no files
///
bool addEmptyDirectoryEntries;
///
/// Apply operations recursively
///
bool recursive;
///
/// Use ZipFile class for listing entries
///
bool useZipFileWhenListing;
///
/// Use relative path information
///
bool relativePathInfo = true;
///
/// Operate silently
///
bool silent;
///
/// Use store rather than deflate when adding files, not likely to be used much
/// but it does exercise the option as the library supports it
///
bool useZipStored;
#if !NETCF
///
/// Restore file date and time to that stored in zip file on extraction
///
bool restoreDateTime;
#endif
///
/// Overwrite files handling
///
Overwrite overwriteFiles = Overwrite.Prompt;
///
/// Optional password for archive
///
string password;
///
/// prefix to remove when creating relative path names
///
string removablePathPrefix;
///
/// Where things will go
///
string targetOutputDirectory;
///
/// What to do based on parsed command line arguments
///
Operation operation = Operation.List;
///
/// Flag indicating wether entry data should be included when testing.
///
bool testData;
///
/// stream used when creating archives.
///
ZipOutputStream outputStream;
#endregion
}
}