diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..4dba023 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,49 @@ +Zip License +----------- + +Copyright (C) 2001 Gerry Shaw + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +Gerry Shaw (gerry_shaw@yahoo.com) + + +zlib License +------------ + +Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +Jean-loup Gailly Mark Adler +jloup@gzip.org madler@alumni.caltech.edu + +http://www.gzip.org/zlib/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..fbd73ca --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# Zip + +Zip is a free .NET Zip library based on the java.util.zip package. It uses the +freely available zlib library for the compression engine and a contribution +to the zlib library for zip file manipulation. + +Note: This is a very old library and I no longer support it. I'm putting it +up on GitHub for historical and keep safe reasons more than anything. That said +it has been used in may products and continues to be of some value. + +## Authors + +* Gerry Shaw + +### Special Thanks + +* The [zlib](http://www.gzip.org/zlib) developers +* Philip Craig for [NUnit](http://nunit.sourceforge.net) + +## License + +BSD diff --git a/Zip.build b/Zip.build new file mode 100644 index 0000000..0a1770e --- /dev/null +++ b/Zip.build @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bin/Zip.Tests.dll b/bin/Zip.Tests.dll new file mode 100644 index 0000000..20ec8a2 Binary files /dev/null and b/bin/Zip.Tests.dll differ diff --git a/bin/Zip.chm b/bin/Zip.chm new file mode 100644 index 0000000..153b507 Binary files /dev/null and b/bin/Zip.chm differ diff --git a/bin/Zip.dll b/bin/Zip.dll new file mode 100644 index 0000000..00b7aa6 Binary files /dev/null and b/bin/Zip.dll differ diff --git a/bin/Zip.xml b/bin/Zip.xml new file mode 100644 index 0000000..3990879 --- /dev/null +++ b/bin/Zip.xml @@ -0,0 +1,684 @@ + + + + Zip + + + + Thrown whenever an error occurs during the build. + + + Constructs an exception with no descriptive information. + + + Constructs an exception with a descriptive message. + The error message that explains the reason for the exception. + + + Constructs an exception with a descriptive message and a reference to the instance of the Exception that is the root cause of the this exception. + The error message that explains the reason for the exception. + An instance of Exception that is the cause of the current Exception. If is non-null, then the current Exception is raised in a catch block handling innerException. + + + Initializes a new instance of the BuildException class with serialized data. + The object that holds the serialized object data. + The contextual information about the source or destination. + + + Specifies how the the zip entry should be compressed. + + + No compression. + + + Default and only supported compression method. + + + Specifies the amount of compression to apply to compressed zip entires. + + + Default compression level. A good choice for speed and size. + + + Do not perfrom compression. + + + Compress the entry as fast as possible size trading size for time. + + + Compress the entry using a balance of size and time. + + + Compress the entry to smallest possible size trading time for size. + + + Represents a entry in a zip file. + + + Initializes a instance of the class with the given name. + The name of entry that will be stored in the directory of the zip file. + + + Creates a new Zip file entry reading values from a zip file. + + + Returns a string representation of the Zip entry. + + + Check if only contains Ascii 8 bit characters. + + + Gets and sets the local file comment for the entry. + + Currently only Ascii 8 bit characters are supported in comments. + A comment cannot exceed 65535 bytes. + + + + Gets the compressed size of the entry data in bytes, or -1 if not known. + + + Gets the CRC-32 checksum of the uncompressed entry data. + + + Gets and sets the optional extra field data for the entry. + ExtraField data cannot exceed 65535 bytes. + + + Gets and sets the default compresion method for zip file entries. See for a list of possible values. + + + Gets and sets the default compresion level for zip file entries. See for a partial list of values. + + + Gets the size of the uncompressed entry data in in bytes. + + + Gets and sets the modification time of the entry. + + + Gets and sets the name of the entry. + + Currently only Ascii 8 bit characters are supported in comments. + A comment cannot exceed 65535 bytes. + + + + Flag that indicates if this entry is a directory or a file. + + + Gets the compression ratio as a percentage. + Returns -1.0 if unknown. + + + Support methods for uncompressing zip files. + + This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g WinZip, InfoZip tools and compatible. + Encryption and multi volume ZipFile (span) are not supported. Old compressions used by old PKZip 1.x are not supported. + Copyright (C) 1998 Gilles Vollant. http://www.winimage.com/zLibDll/unzip.htm + C# wrapper by Gerry Shaw (gerry_shaw@yahoo.com). http://www.organicbit.com/zip/ + + + + Create a zip file. + + + Open a new zip entry for writing. + + + Write data to the zip file. + + + Close the current entry in the zip file. + + + Close the zip file. + + + Opens a zip file for reading. + The name of the zip to open. At this time only file names with ANSI (8 bit) characters are supported. + + A handle usable with other functions of the ZipLib class. + Otherwise IntPtr.Zero if the zip file could not e opened (file doen not exist or is not valid). + + + + Closes a zip file opened with unzipOpen. + The zip file handle opened by . + If there are files inside the zip file opened with these files must be closed with before call unzClose. + + Zero if there was no error. + Otherwise a value less than zero. See for the specific reason. + + + + Get global information about the zip file. + The zip file handle opened by . + An address of a struct to hold the information. No preparation of the structure is needed. + + Zero if there was no error. + Otherwise a value less than zero. See for the specific reason. + + + + Get the comment associated with the entire zip file. + The zip file handle opened by + The buffer to hold the comment. + The length of the buffer in bytes (8 bit characters). + + The number of characters in the comment if there was no error. + Otherwise a value less than zero. See for the specific reason. + + + + Set the current file of the zip file to the first file. + The zip file handle opened by . + + Zero if there was no error. + Otherwise a value less than zero. See for the specific reason. + + + + Set the current file of the zip file to the next file. + The zip file handle opened by . + + Zero if there was no error. + Otherwise if there are no more entries. + + + + Try locate the entry in the zip file. + The zip file handle opened by . + The name of the entry to look for. + If 0 use the OS default. If 1 use case sensitivity like strcmp, Unix style. If 2 do not use case sensitivity like strcmpi, Windows style. + + Zero if there was no error. + Otherwise if there are no more entries. + + + + Get information about the current entry in the zip file. + The zip file handle opened by . + A ZipEntryInfo struct to hold information about the entry or null. + An array of sbyte characters to hold the entry name or null. + The length of the entryNameBuffer in bytes. + An array to hold the extra field data for the entry or null. + The length of the extraField array in bytes. + An array of sbyte characters to hold the entry name or null. + The length of theh commentBuffer in bytes. + + If entryInfoPtr is not null the structure will contain information about the current file. + If entryNameBuffer is not null the name of the entry will be copied into it. + If extraField is not null the extra field data of the entry will be copied into it. + If commentBuffer is not null the comment of the entry will be copied into it. + + + Zero if there was no error. + Otherwise a value less than zero. See for the specific reason. + + + + Open the zip file entry for reading. + The zip file handle opened by . + + Zero if there was no error. + Otherwise a value from . + + + + Close the file entry opened by . + The zip file handle opened by . + + Zero if there was no error. + CrcError if the file was read but the Crc does not match. + Otherwise a value from . + + + + Read bytes from the current zip file entry. + The zip file handle opened by . + Buffer to store the uncompressed data into. + Number of bytes to write from . + + The number of byte copied if somes bytes are copied. + Zero if the end of file was reached. + Less than zero with error code if there is an error. See for a list of possible error codes. + + + + Give the current position in uncompressed data of the zip file entry currently opened. + The zip file handle opened by . + The number of bytes into the uncompressed data read so far. + + + Determine if the end of the zip file entry has been reached. + The zip file handle opened by . + + One if the end of file was reached. + Zero if elsewhere. + + + + Converts a CLR string to a 8 bit ANSI array of characters. + The string to convert. + A 8 bit ANSI array of characters. + + + Converst an 8 bit ANSI C style string to a CLR string. + The array of a characters that holds the string. + The CLR string representing the characters passed in. + + + List of possible error codes. + + + No error. + + + Unknown error. + + + Last entry in directory reached. + + + Parameter error. + + + Zip file is invalid. + + + Internal program error. + + + Crc values do not match. + + + Global information about the zip file. + + + The number of entries in the directory. + + + Length of zip file comment in bytes (8 bit characters). + + + Custom ZipLib date time structure. + + + Seconds after the minute - [0,59] + + + Minutes after the hour - [0,59] + + + Hours since midnight - [0,23] + + + Day of the month - [1,31] + + + Months since January - [0,11] + + + Years - [1980..2044] + + + Information stored in zip file directory about an entry. + + + Version needed to extract (2 bytes). + + + General purpose bit flag (2 bytes). + + + Compression method (2 bytes). + + + Last mod file date in Dos fmt (4 bytes). + + + Crc-32 (4 bytes). + + + Compressed size (4 bytes). + + + Uncompressed size (4 bytes). + + + Filename length (2 bytes). + + + Extra field length (2 bytes). + + + File comment length (2 bytes). + + + Disk number start (2 bytes). + + + Internal file attributes (2 bytes). + + + External file attributes (4 bytes). + + + File modification date of entry. + + + A collection that stores objects. + + + + Initializes a new instance of . + + + Initializes a new instance of based on another . + A from which the contents are copied. + + + Initializes a new instance of containing any array of objects. + A array of objects with which to intialize the collection. + + + Adds a with the specified value to the . + The to add. + The index at which the new element was inserted. + + + + Copies the elements of an array to the end of the . + An array of type containing the objects to add to the collection. + None. + + + + Adds the contents of another to the end of the collection. + A containing the objects to add to the collection. + None. + + + + Gets a value indicating whether the contains the specified . + The to locate. + + if the is contained in the collection; + otherwise, . + + + + + Copies the values to a one-dimensional instance at the specified index. + The one-dimensional that is the destination of the values copied from . + The index in where copying begins. + None. + is multidimensional. -or- The number of elements in the is greater than the available space between and the end of . + is . + is less than 's lowbound. + + + + Returns the index of a in the . + The to locate. + + The index of the of in the + , if found; otherwise, -1. + + + + + Inserts a into the at the specified index. + The zero-based index where should be inserted. + The to insert. + None. + + + + Returns an enumerator that can iterate through the . + None. + + + + Removes a specific from the . + The to remove from the . + None. + is not found in the Collection. + + + Represents the entry at the specified index of the . + The zero-based index of the entry to locate in the collection. + + The entry at the specified index of the collection. + + is outside the valid range of indexes for the collection. + + + Enumerator for . + + + Initializes a new instance of the class. + + + Advance the enumerator to the next entry in the collection. + true if there are more entries; false if there are no more entires in the collection. + + + Set the enumerator to just before the start of the collection. Call to advance to the first entry in the collection. + + + Gets the current entry. + + + Provides support for reading files in the ZIP file format. Includes support for both compressed and uncompressed entries. + This example shows how to view the entries in a ZIP file. + + public static void View(string zipFileName) { + ZipReader reader = new ZipReader(zipFileName); + + Console.WriteLine("Archive: {0} ({1} files)", zipFileName, reader.Entries.Count); + Console.WriteLine(reader.Comment); + + string format = "{0,8} {1,8} {2,5} {3,10} {4,5} {5}"; + Console.WriteLine(format, " Length ", " Size ", "Ratio", " Date ", "Time ", "Name"); + Console.WriteLine(format, "--------", "--------", "-----", "----------", "-----", "----"); + + foreach (ZipEntry entry in reader.Entries) { + if (!entry.IsDirectory) { + Console.WriteLine(format, + entry.Length, + entry.CompressedLength, + entry.Ratio.ToString("P0"), + entry.ModifiedTime.ToString("yyyy-MM-dd"), + entry.ModifiedTime.ToString("hh:mm"), + entry.Name); + } + } + reader.Close(); + } + + + This example shows how to extract files from a ZIP file. + + public static void Extract(string zipFileName) { + ZipReader reader = new ZipReader(zipFileName); + Console.WriteLine("Archive: {0}", zipFileName); + Console.WriteLine(reader.Comment); + + // buffer to hold temp bytes + byte[] buffer = new byte[4096]; + int byteCount; + + // Get the zipped entries + while (reader.MoveNext()) { + ZipEntry entry = reader.Current; + + if (entry.IsDirectory) { + Directory.CreateDirectory(entry.Name); + } else { + Console.Write(" {0}", entry.Name); + + // create output stream + FileStream writer = File.Open(entry.Name, FileMode.Create); + + // write uncompressed data + while ((byteCount = reader.Read(buffer, 0, buffer.Length)) > 0) { + Console.Write("."); + writer.Write(buffer, 0, byteCount); + } + writer.Close(); + Console.WriteLine(); + } + } + reader.Close(); + } + + + + + ZipFile handle to read data from. + + + Name of zip file. + + + Contents of zip file directory. + + + Global zip file comment. + + + True if an entry is open for reading. + + + Current zip entry open for reading. + + + Initializes a instance of the class for reading the zip file with the given name. + The name of zip file that will be read. + + + Cleans up the resources used by this zip file. + + + Dispose is synonym for Close. + + + Closes the zip file and releases any resources. + + + Advances the enumerator to the next element of the collection. + Sets to the next zip entry. + true if the next entry is not null; otherwise false. + + + Move to just before the first entry in the zip directory. + + + Seek to the specified entry. + The name of the entry to seek to. + + + Uncompress a block of bytes from the current zip entry and writes the data in a given buffer. + The array to write data into. + The byte offset in at which to begin writing. + The maximum number of bytes to read. + + + Gets the name of the zip file that was passed to the constructor. + + + Gets the global comment for the zip file. + + + Gets a object that contains all the entries in the zip file directory. + + + Gets the current entry in the zip file.. + + + Provides support for writing files in the ZIP file format. Includes support for both compressed and uncompressed entries. + This example shows how to create a ZIP file. + + public static void Add(string zipFileName, string[] entryPatterns) { + string currentDirectory = Directory.GetCurrentDirectory(); + Console.WriteLine("Creating {0}", zipFileName); + + ZipWriter writer = new ZipWriter(zipFileName); + + // buffer to hold temp bytes + byte[] buffer = new byte[4096]; + int byteCount; + + // add files to archive + foreach (string pattern in entryPatterns) { + foreach (string path in Directory.GetFiles(currentDirectory, pattern)) { + string fileName = Path.GetFileName(path); + Console.Write("Adding {0}", fileName); + + ZipEntry entry = new ZipEntry(fileName); + entry.ModifiedTime = File.GetLastWriteTime(fileName); + entry.Comment = "local file comment"; + + writer.AddEntry(entry); + + FileStream reader = File.OpenRead(entry.Name); + while ((byteCount = reader.Read(buffer, 0, buffer.Length)) > 0) { + Console.Write("."); + writer.Write(buffer, 0, byteCount); + } + reader.Close(); + Console.WriteLine(); + } + } + + writer.Close(); + } + + + + + Name of the zip file. + + + Zip file global comment. + + + True if currently writing a new zip file entry. + + + Zip file handle. + + + Initializes a new instance fo the class with a specified file name. Any Existing file will be overwritten. + The name of the zip file to create. + + + Cleans up the resources used by this zip file. + + + Dispose is synonym for Close. + + + Closes the zip file and releases any resources. + + + Creates a new zip entry in the directory and positions the stream to the start of the entry data. + The zip entry to be written. + Closes the current entry if still active. + + + Compress a block of bytes from the given buffer and writes them into the current zip entry. + The array to read data from. + The byte offset in at which to begin reading. + The maximum number of bytes to write. + + + Gets the name of the zip file. + + + Gets and sets the zip file comment. + + + diff --git a/bin/zlib.dll b/bin/zlib.dll new file mode 100644 index 0000000..d106ac6 Binary files /dev/null and b/bin/zlib.dll differ diff --git a/examples/MiniZip/MiniZip.cs b/examples/MiniZip/MiniZip.cs new file mode 100644 index 0000000..218b5c5 --- /dev/null +++ b/examples/MiniZip/MiniZip.cs @@ -0,0 +1,196 @@ +// Copyright (C) 2001 Gerry Shaw +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// +// Gerry Shaw (gerry_shaw@yahoo.com) + +using System; +using System.IO; +using OrganicBit.Zip; + +namespace OrganicBit.Zip.Examples { + + public class MiniZip { + + public static void ShowHelp() { + Console.WriteLine( +@"MiniZip - A simple zip file manipulator +http://www.organicbit.com/zip/ + +Usage: minizip [-v|-e|-a] [files to add] + +Examples: + minizip # displays this message + minizip -v # view contents + minizip -e # extract files + minizip -a [*.txt] # add files +"); + } + + public static void View(string zipFileName) { + ZipReader reader = new ZipReader(zipFileName); + + Console.WriteLine("Archive: {0} ({1} files)", zipFileName, reader.Entries.Count); + Console.WriteLine(reader.Comment); + + string format = "{0,8} {1,8} {2,5} {3,10} {4,5} {5}"; + Console.WriteLine(format, " Length ", " Size ", "Ratio", " Date ", "Time ", "Name"); + Console.WriteLine(format, "--------", "--------", "-----", "----------", "-----", "----"); + + foreach (ZipEntry entry in reader.Entries) { + if (!entry.IsDirectory) { + Console.WriteLine(format, + entry.Length, + entry.CompressedLength, + entry.Ratio.ToString("P0"), + entry.ModifiedTime.ToString("yyyy-MM-dd"), + entry.ModifiedTime.ToString("hh:mm"), + entry.Name); + } + } + reader.Close(); + } + + public static void Extract(string zipFileName) { + ZipReader reader = new ZipReader(zipFileName); + Console.WriteLine("Archive: {0}", zipFileName); + Console.WriteLine(reader.Comment); + + // buffer to hold temp bytes + byte[] buffer = new byte[4096]; + int byteCount; + + // Get the zipped entries + while (reader.MoveNext()) { + ZipEntry entry = reader.Current; + + if (entry.IsDirectory) { + Directory.CreateDirectory(entry.Name); + } else { + Console.Write(" {0}", entry.Name); + + // create output stream + FileStream writer = File.Open(entry.Name, FileMode.Create); + + // write uncompressed data + while ((byteCount = reader.Read(buffer, 0, buffer.Length)) > 0) { + Console.Write("."); + writer.Write(buffer, 0, byteCount); + } + writer.Close(); + Console.WriteLine(); + } + } + reader.Close(); + } + + public static void Add(string zipFileName, string[] entryPatterns) { + string currentDirectory = Directory.GetCurrentDirectory(); + Console.WriteLine("Creating {0}", zipFileName); + + ZipWriter writer = new ZipWriter(zipFileName); + + // buffer to hold temp bytes + byte[] buffer = new byte[4096]; + int byteCount; + + // add files to archive + foreach (string pattern in entryPatterns) { + foreach (string path in Directory.GetFiles(currentDirectory, pattern)) { + string fileName = Path.GetFileName(path); + Console.Write("Adding {0}", fileName); + + ZipEntry entry = new ZipEntry(fileName); + entry.ModifiedTime = File.GetLastWriteTime(fileName); + entry.Comment = "local file comment"; + + writer.AddEntry(entry); + + FileStream reader = File.OpenRead(entry.Name); + while ((byteCount = reader.Read(buffer, 0, buffer.Length)) > 0) { + Console.Write("."); + writer.Write(buffer, 0, byteCount); + } + reader.Close(); + Console.WriteLine(); + } + } + + writer.Close(); + } + + enum Action { + Nothing, + ShowHelp, + View, + Extract, + Add + } + + static Action ParseCommandLine(string[] args) { + Action action = Action.ShowHelp; + if (args.Length >= 2) { + if (args[0][0] == '-') { + switch (args[0][1]) { + case 'v': + action = Action.View; + break; + + case 'e': + action = Action.Extract; + break; + + case 'a': + action = Action.Add; + break; + } + } + } + return action; + } + + public static int Main(string[] args) { + try { + Action action = ParseCommandLine(args); + switch (action) { + case Action.ShowHelp: + ShowHelp(); + break; + + case Action.View: + View(args[1]); + break; + + case Action.Extract: + Extract(args[1]); + break; + + case Action.Add: + string[] patterns = new string[args.Length - 2]; + Array.Copy(args, 2, patterns, 0, args.Length - 2); + Add(args[1], patterns); + break; + } + } catch (Exception e) { + Console.WriteLine(e.Message); + Environment.Exit(1); + } + + return 0; + } + } +} diff --git a/src/Zip.Tests/AllTests.cs b/src/Zip.Tests/AllTests.cs new file mode 100644 index 0000000..06a4a1f --- /dev/null +++ b/src/Zip.Tests/AllTests.cs @@ -0,0 +1,53 @@ +// Copyright (C) 2001 Gerry Shaw +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// +// Gerry Shaw (gerry_shaw@yahoo.com) + +using System; +using System.Reflection; +using NUnit.Framework; + +namespace OrganicBit.Zip.Tests { + + // This class bundles all our tests into a single suite. If you wanted + // you could create other suites with a sub set of the tests. All that is + // required is a property called Suite that returns a ITest object. The + // ITest object most commonly returned is a TestSuite. For single class + // tests this member can be included within the TestCase. + public class AllTests { + public static ITest Suite { + get { + // Use reflection to automagically scan all the classes that + // inherit from TestCase and add them to the suite. + TestSuite suite = new TestSuite("Zip Tests"); + /* + Assembly assembly = Assembly.GetExecutingAssembly(); + foreach(Type type in assembly.GetTypes()) { + if (type.IsSubclassOf(typeof(TestCase)) && !type.IsAbstract) { + suite.AddTestSuite(type); + } + } + */ + suite.AddTestSuite(typeof(ZipExceptionTest)); + suite.AddTestSuite(typeof(ZipFileTest)); + suite.AddTestSuite(typeof(ZLibTest)); + return suite; + } + } + } +} diff --git a/src/Zip.Tests/ExceptionTest.cs b/src/Zip.Tests/ExceptionTest.cs new file mode 100644 index 0000000..d9dc805 --- /dev/null +++ b/src/Zip.Tests/ExceptionTest.cs @@ -0,0 +1,48 @@ +// Copyright (C) 2001 Gerry Shaw +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// +// Gerry Shaw (gerry_shaw@yahoo.com) + +using System; +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; +using NUnit.Framework; + +namespace OrganicBit.Zip.Tests { + + // By Eric Gunnerson (Microsoft Corporation) + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp08162001.asp + internal class ExceptionTest { + public static void TestSerialization(Exception e) { + string tempFileName = Path.GetTempFileName(); + + Stream streamRead = null; + Stream streamWrite = File.Create(tempFileName); + BinaryFormatter binaryWrite = new BinaryFormatter(); + binaryWrite.Serialize(streamWrite, e); + streamWrite.Close(); + + streamRead = File.OpenRead(tempFileName); + BinaryFormatter binaryRead = new BinaryFormatter(); + object oout = binaryRead.Deserialize(streamRead); + streamRead.Close(); + + File.Delete(tempFileName); + } + } +} diff --git a/src/Zip.Tests/ZLibTest.cs b/src/Zip.Tests/ZLibTest.cs new file mode 100644 index 0000000..a76e47e --- /dev/null +++ b/src/Zip.Tests/ZLibTest.cs @@ -0,0 +1,45 @@ +// Copyright (C) 2001 Gerry Shaw +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// +// Gerry Shaw (gerry_shaw@yahoo.com) + +using System; +using OrganicBit.Zip; +using NUnit.Framework; + +namespace OrganicBit.Zip.Tests { + + public class ZLibTest : TestCase { + + public ZLibTest(String name) : base(name) { + } + + protected override void SetUp() { + } + + protected override void TearDown() { + } + + public void Test_Version() { + try { + //Assert(ZLib.Version == "1.1.3"); + } catch (Exception) { + } + } + } +} \ No newline at end of file diff --git a/src/Zip.Tests/ZipExceptionTest.cs b/src/Zip.Tests/ZipExceptionTest.cs new file mode 100644 index 0000000..a12be72 --- /dev/null +++ b/src/Zip.Tests/ZipExceptionTest.cs @@ -0,0 +1,48 @@ +// Copyright (C) 2001 Gerry Shaw +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// +// Gerry Shaw (gerry_shaw@yahoo.com) + +using System; +using OrganicBit.Zip; +using NUnit.Framework; + +namespace OrganicBit.Zip.Tests { + + public class ZipExceptionTest : TestCase { + + public ZipExceptionTest(String name) : base(name) { + } + + protected override void SetUp() { + } + + protected override void TearDown() { + } + + public void Test_Serialization() { + try { + // getting a BadFormatException when accessing anything in Zip.dll + // new ZipException(); + } catch (Exception e) { + Console.WriteLine(e.ToString()); + } + //ExceptionTest.TestSerialization(new ZipException()); + } + } +} diff --git a/src/Zip.Tests/ZipFileTest.cs b/src/Zip.Tests/ZipFileTest.cs new file mode 100644 index 0000000..01c0c92 --- /dev/null +++ b/src/Zip.Tests/ZipFileTest.cs @@ -0,0 +1,49 @@ +// Copyright (C) 2001 Gerry Shaw +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// +// Gerry Shaw (gerry_shaw@yahoo.com) + +using System; +using OrganicBit.Zip; +using NUnit.Framework; + +namespace OrganicBit.Zip.Tests { + + public class ZipFileTest : TestCase { + + public ZipFileTest(String name) : base(name) { + } + + protected override void SetUp() { + } + + protected override void TearDown() { + } + + public void Test_Loading() { + /* + ZipFile zip = new ZipFile("test.zip"); + Console.WriteLine(zip.ToString()); + + foreach (ZipEntry entry in zip.Entries) { + Console.WriteLine(entry.ToString()); + } + */ + } + } +} \ No newline at end of file diff --git a/src/Zip.Tests/data/test.zip b/src/Zip.Tests/data/test.zip new file mode 100644 index 0000000..95d53c6 Binary files /dev/null and b/src/Zip.Tests/data/test.zip differ diff --git a/src/Zip/AssemblyInfo.cs b/src/Zip/AssemblyInfo.cs new file mode 100644 index 0000000..f6d7ea7 --- /dev/null +++ b/src/Zip/AssemblyInfo.cs @@ -0,0 +1,39 @@ +// Copyright (C) 2001 Gerry Shaw +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// +// Gerry Shaw (gerry_shaw@yahoo.com) + +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("Zip")] +[assembly: AssemblyDescription("A .NET Zip library")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("http://www.organicbit.com/zip/")] +[assembly: AssemblyProduct("NAnt")] +[assembly: AssemblyCopyright("Copyright (C) 2001 Gerry Shaw")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: AssemblyVersion("0.9.0.*")] + +// This will not compile with Visual Studio. If you want to build a signed +// executable use the NAnt build file. To build under Visual Studio just +// exclude this file from the build. +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile(@"..\Zip.key")] +//[assembly: AssemblyKeyName("")] diff --git a/src/Zip/ZipEntry.cs b/src/Zip/ZipEntry.cs new file mode 100644 index 0000000..0a73544 --- /dev/null +++ b/src/Zip/ZipEntry.cs @@ -0,0 +1,245 @@ +// Copyright (C) 2001 Gerry Shaw +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// +// Gerry Shaw (gerry_shaw@yahoo.com) + +using System; +using System.IO; + +namespace OrganicBit.Zip { + + /// Specifies how the the zip entry should be compressed. + public enum CompressionMethod { + /// No compression. + Stored = 0, + + /// Default and only supported compression method. + Deflated = 8 + } + + /// Specifies the amount of compression to apply to compressed zip entires. + public enum CompressionLevel : int { + /// Default compression level. A good choice for speed and size. + Default = -1, + + /// Do not perfrom compression. + None = 0, + + /// Compress the entry as fast as possible size trading size for time. + Fastest = 1, + + /// Compress the entry using a balance of size and time. + Average = 5, + + /// Compress the entry to smallest possible size trading time for size. + Smallest = 9 + } + + /// Represents a entry in a zip file. + public class ZipEntry { + + string _name = String.Empty; + uint _crc = 0; + long _compressedLength = -1; + long _uncompressedLength = -1; + byte[] _extraField = null; + string _comment = String.Empty; + DateTime _modifiedTime = DateTime.Now; + + CompressionMethod _method = CompressionMethod.Deflated; + int _level = (int) CompressionLevel.Default; + + /// Initializes a instance of the class with the given name. + /// The name of entry that will be stored in the directory of the zip file. + public ZipEntry(string name) { + Name = name; + } + + /// Creates a new Zip file entry reading values from a zip file. + internal ZipEntry(IntPtr handle) { + ZipEntryInfo entryInfo; + int result = 0; + unsafe { + result = ZipLib.unzGetCurrentFileInfo(handle, &entryInfo, null, 0, null, 0, null, 0); + } + if (result != 0) { + throw new ZipException("Could not read entries from zip file " + Name); + } + + ExtraField = new byte[entryInfo.ExtraFieldLength]; + sbyte[] entryNameBuffer = new sbyte[entryInfo.FileNameLength]; + sbyte[] commentBuffer = new sbyte[entryInfo.CommentLength]; + + unsafe { + result = ZipLib.unzGetCurrentFileInfo(handle, &entryInfo, + entryNameBuffer, (uint) entryNameBuffer.Length, + ExtraField, (uint) ExtraField.Length, + commentBuffer, (uint) commentBuffer.Length); + } + if (result != 0) { + throw new ZipException("Could not read entries from zip file " + Name); + } + + _name = ZipLib.AnsiToString(entryNameBuffer); + _comment = ZipLib.AnsiToString(commentBuffer); + + _crc = entryInfo.Crc; + _compressedLength = entryInfo.CompressedSize; + _uncompressedLength = entryInfo.UncompressedSize; + _method = (CompressionMethod) entryInfo.CompressionMethod; + _modifiedTime = new DateTime( + (int) entryInfo.DateTime.Year, + (int) entryInfo.DateTime.Month + 1, + (int) entryInfo.DateTime.Day, + (int) entryInfo.DateTime.Hours, + (int) entryInfo.DateTime.Minutes, + (int) entryInfo.DateTime.Seconds); + } + + /// Gets and sets the local file comment for the entry. + /// + /// Currently only Ascii 8 bit characters are supported in comments. + /// A comment cannot exceed 65535 bytes. + /// + public string Comment { + get { return _comment; } + set { + // null comments are valid + if (value != null) { + if (value.Length > 0xffff) { + throw new ArgumentOutOfRangeException("Comment cannot not exceed 65535 characters."); + } + if (!IsAscii(value)) { + throw new ArgumentException("Name can only contain Ascii 8 bit characters."); + } + } + + // TODO: check for ASCII only characters + _comment = value; + } + } + + /// Gets the compressed size of the entry data in bytes, or -1 if not known. + public long CompressedLength { + get { return _compressedLength; } + } + + /// Gets the CRC-32 checksum of the uncompressed entry data. + public uint Crc { + get { return _crc; } + } + + /// Gets and sets the optional extra field data for the entry. + /// ExtraField data cannot exceed 65535 bytes. + public byte[] ExtraField { + get { + return _extraField; + } + set { + if (value.Length > 0xffff) { + throw new ArgumentOutOfRangeException("ExtraField cannot not exceed 65535 bytes."); + } + _extraField = value; + } + } + + /// Gets and sets the default compresion method for zip file entries. See for a list of possible values. + public CompressionMethod Method { + get { return _method; } + set { _method = value; } + } + + /// Gets and sets the default compresion level for zip file entries. See for a partial list of values. + public int Level { + get { return _level; } + set { + if (value < -1 || value > 9) { + throw new ArgumentOutOfRangeException("Level", value, "Level value must be between -1 and 9."); + } + _level = value; + } + } + + /// Gets the size of the uncompressed entry data in in bytes. + public long Length { + get { return _uncompressedLength; } + } + + /// Gets and sets the modification time of the entry. + public DateTime ModifiedTime { + get { return _modifiedTime; } + set { _modifiedTime = value; } + } + + /// Gets and sets the name of the entry. + /// + /// Currently only Ascii 8 bit characters are supported in comments. + /// A comment cannot exceed 65535 bytes. + /// + public string Name { + get { return _name; } + set { + if (value == null) { + throw new ArgumentNullException("Name cannot be null."); + } + if (value.Length > 0xffff) { + throw new ArgumentOutOfRangeException("Name cannot not exceed 65535 characters."); + } + if (!IsAscii(value)) { + throw new ArgumentException("Name can only contain Ascii 8 bit characters."); + } + // TODO: check for ASCII only characters + _name = value; + } + } + + /// Flag that indicates if this entry is a directory or a file. + public bool IsDirectory { + get { + return (Length == 0 && CompressedLength == 0); + } + } + + /// Gets the compression ratio as a percentage. + /// Returns -1.0 if unknown. + public float Ratio { + get { + float ratio = -1.0f; + if (Length > 0) { + ratio = Convert.ToSingle(Length - CompressedLength) / Length; + } + return ratio; + } + } + + /// Returns a string representation of the Zip entry. + public override string ToString() { + return String.Format("{0} {1}", Name, base.ToString()); + } + + /// Check if only contains Ascii 8 bit characters. + static bool IsAscii(string str) { + foreach (char ch in str) { + if (ch > 0xff) { + return false; + } + } + return true; + } + } +} diff --git a/src/Zip/ZipEntryCollection.cs b/src/Zip/ZipEntryCollection.cs new file mode 100644 index 0000000..78102de --- /dev/null +++ b/src/Zip/ZipEntryCollection.cs @@ -0,0 +1,195 @@ +// Copyright (C) 2001 Gerry Shaw +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// +// Gerry Shaw (gerry_shaw@yahoo.com) + +using System; +using System.Collections; + +namespace OrganicBit.Zip { + + /// A collection that stores objects. + /// + [Serializable()] + public class ZipEntryCollection : CollectionBase { + + /// Initializes a new instance of . + public ZipEntryCollection() { + } + + /// Initializes a new instance of based on another . + /// A from which the contents are copied. + public ZipEntryCollection(ZipEntryCollection value) { + this.AddRange(value); + } + + /// Initializes a new instance of containing any array of objects. + /// A array of objects with which to intialize the collection. + public ZipEntryCollection(ZipEntry[] value) { + this.AddRange(value); + } + + /// Represents the entry at the specified index of the . + /// The zero-based index of the entry to locate in the collection. + /// + /// The entry at the specified index of the collection. + /// + /// is outside the valid range of indexes for the collection. + public ZipEntry this[int index] { + get { + return ((ZipEntry)(List[index])); + } + set { + List[index] = value; + } + } + + /// Adds a with the specified value to the . + /// The to add. + /// The index at which the new element was inserted. + /// + public int Add(ZipEntry value) { + return List.Add(value); + } + + /// Copies the elements of an array to the end of the . + /// An array of type containing the objects to add to the collection. + /// None. + /// + public void AddRange(ZipEntry[] value) { + for (int i = 0; (i < value.Length); i = (i + 1)) { + this.Add(value[i]); + } + } + + /// Adds the contents of another to the end of the collection. + /// A containing the objects to add to the collection. + /// None. + /// + public void AddRange(ZipEntryCollection value) { + for (int i = 0; (i < value.Count); i = (i + 1)) { + this.Add(value[i]); + } + } + + /// Gets a value indicating whether the contains the specified . + /// The to locate. + /// + /// if the is contained in the collection; + /// otherwise, . + /// + /// + public bool Contains(ZipEntry value) { + return List.Contains(value); + } + + /// Copies the values to a one-dimensional instance at the specified index. + /// The one-dimensional that is the destination of the values copied from . + /// The index in where copying begins. + /// None. + /// is multidimensional. -or- The number of elements in the is greater than the available space between and the end of . + /// is . + /// is less than 's lowbound. + /// + public void CopyTo(ZipEntry[] array, int index) { + List.CopyTo(array, index); + } + + /// Returns the index of a in the . + /// The to locate. + /// + /// The index of the of in the + /// , if found; otherwise, -1. + /// + /// + public int IndexOf(ZipEntry value) { + return List.IndexOf(value); + } + + /// Inserts a into the at the specified index. + /// The zero-based index where should be inserted. + /// The to insert. + /// None. + /// + public void Insert(int index, ZipEntry value) { + List.Insert(index, value); + } + + /// Returns an enumerator that can iterate through the . + /// None. + /// + public new ZipEntryEnumerator GetEnumerator() { + return new ZipEntryEnumerator(this); + } + + /// Removes a specific from the . + /// The to remove from the . + /// None. + /// is not found in the Collection. + public void Remove(ZipEntry value) { + List.Remove(value); + } + + /// Enumerator for . + public class ZipEntryEnumerator : object, IEnumerator { + + private IEnumerator baseEnumerator; + + private IEnumerable temp; + + /// Initializes a new instance of the class. + public ZipEntryEnumerator(ZipEntryCollection mappings) { + this.temp = ((IEnumerable)(mappings)); + this.baseEnumerator = temp.GetEnumerator(); + } + + /// Gets the current entry. + public ZipEntry Current { + get { + return ((ZipEntry)(baseEnumerator.Current)); + } + } + + object IEnumerator.Current { + get { + return baseEnumerator.Current; + } + } + + + /// Advance the enumerator to the next entry in the collection. + /// true if there are more entries; false if there are no more entires in the collection. + public bool MoveNext() { + return baseEnumerator.MoveNext(); + } + + bool IEnumerator.MoveNext() { + return baseEnumerator.MoveNext(); + } + + /// Set the enumerator to just before the start of the collection. Call to advance to the first entry in the collection. + public void Reset() { + baseEnumerator.Reset(); + } + + void IEnumerator.Reset() { + baseEnumerator.Reset(); + } + } + } +} diff --git a/src/Zip/ZipException.cs b/src/Zip/ZipException.cs new file mode 100644 index 0000000..eac22f1 --- /dev/null +++ b/src/Zip/ZipException.cs @@ -0,0 +1,51 @@ +// Copyright (C) 2001 Gerry Shaw +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// +// Gerry Shaw (gerry_shaw@yahoo.com) + +using System; +using System.Runtime.Serialization; + +namespace OrganicBit.Zip { + + /// Thrown whenever an error occurs during the build. + [Serializable] + public class ZipException : ApplicationException { + + /// Constructs an exception with no descriptive information. + public ZipException() : base() { + } + + /// Constructs an exception with a descriptive message. + /// The error message that explains the reason for the exception. + public ZipException(String message) : base(message) { + } + + /// Constructs an exception with a descriptive message and a reference to the instance of the Exception that is the root cause of the this exception. + /// The error message that explains the reason for the exception. + /// An instance of Exception that is the cause of the current Exception. If is non-null, then the current Exception is raised in a catch block handling innerException. + public ZipException(String message, Exception innerException) : base(message, innerException) { + } + + /// Initializes a new instance of the BuildException class with serialized data. + /// The object that holds the serialized object data. + /// The contextual information about the source or destination. + public ZipException(SerializationInfo info, StreamingContext context) : base(info, context) { + } + } +} diff --git a/src/Zip/ZipLib.cs b/src/Zip/ZipLib.cs new file mode 100644 index 0000000..310c956 --- /dev/null +++ b/src/Zip/ZipLib.cs @@ -0,0 +1,378 @@ +// Copyright (C) 2001 Gerry Shaw +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// +// Gerry Shaw (gerry_shaw@yahoo.com) + +using System; +using System.IO; +using System.Text; +using System.Runtime.InteropServices; + +namespace OrganicBit.Zip { + + /// Support methods for uncompressing zip files. + /// + /// This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g WinZip, InfoZip tools and compatible. + /// Encryption and multi volume ZipFile (span) are not supported. Old compressions used by old PKZip 1.x are not supported. + /// Copyright (C) 1998 Gilles Vollant. http://www.winimage.com/zLibDll/unzip.htm + /// C# wrapper by Gerry Shaw (gerry_shaw@yahoo.com). http://www.organicbit.com/zip/ + /// + internal sealed class ZipLib { + + // prevent instances of this class from being constructed + private ZipLib() {} + + /* + Create a zipfile. + pathname contain on Windows NT a filename like "c:\\zlib\\zlib111.zip" or on an Unix computer "zlib/zlib111.zip". + if the file pathname exist and append=1, the zip will be created at the end of the file. (useful if the file contain a self extractor code) + If the zipfile cannot be opened, the return value is NULL. + Else, the return value is a zipFile Handle, usable with other function of this zip package. + */ + /// Create a zip file. + [DllImport("zlib.dll", ExactSpelling=true, CharSet=CharSet.Ansi)] + public static extern IntPtr zipOpen(string fileName, int append); + + /* + Open a file in the ZIP for writing. + filename : the filename in zip (if NULL, '-' without quote will be used + *zipfi contain supplemental information + if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local contains the extrafield data the the local header + if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global contains the extrafield data the the local header + if comment != NULL, comment contain the comment string + method contain the compression method (0 for store, Z_DEFLATED for deflate) + level contain the level of compression (can be Z_DEFAULT_COMPRESSION) + */ + /// Open a new zip entry for writing. + [DllImport("zlib.dll", ExactSpelling=true, CharSet=CharSet.Ansi)] + unsafe public static extern int zipOpenNewFileInZip(IntPtr handle, + string entryName, + ZipFileEntryInfo* entryInfoPtr, + byte[] extraField, + uint extraFieldLength, + byte[] extraFieldGlobal, + uint extraFieldGlobalLength, + string comment, + int method, + int level); + + + /// Write data to the zip file. + [DllImport("zlib.dll")] + public static extern int zipWriteInFileInZip(IntPtr handle, byte[] buffer, uint count); + + /// Close the current entry in the zip file. + [DllImport("zlib.dll")] + public static extern int zipCloseFileInZip(IntPtr handle); + + /// Close the zip file. + [DllImport("zlib.dll", ExactSpelling=true, CharSet=CharSet.Ansi)] + public static extern int zipClose(IntPtr handle, string comment); + + + /// Opens a zip file for reading. + /// The name of the zip to open. At this time only file names with ANSI (8 bit) characters are supported. + /// + /// A handle usable with other functions of the ZipLib class. + /// Otherwise IntPtr.Zero if the zip file could not e opened (file doen not exist or is not valid). + /// + [DllImport("zlib.dll", ExactSpelling=true, CharSet=CharSet.Ansi)] + public static extern IntPtr unzOpen(string fileName); + + /// Closes a zip file opened with unzipOpen. + /// The zip file handle opened by . + /// If there are files inside the zip file opened with these files must be closed with before call unzClose. + /// + /// Zero if there was no error. + /// Otherwise a value less than zero. See for the specific reason. + /// + [DllImport("zlib.dll")] + public static extern int unzClose(IntPtr handle); + + /// Get global information about the zip file. + /// The zip file handle opened by . + /// An address of a struct to hold the information. No preparation of the structure is needed. + /// + /// Zero if there was no error. + /// Otherwise a value less than zero. See for the specific reason. + /// + [DllImport("zlib.dll")] + public unsafe static extern int unzGetGlobalInfo(IntPtr handle, ZipFileInfo* globalInfoPtr); + + /// Get the comment associated with the entire zip file. + /// The zip file handle opened by + /// The buffer to hold the comment. + /// The length of the buffer in bytes (8 bit characters). + /// + /// The number of characters in the comment if there was no error. + /// Otherwise a value less than zero. See for the specific reason. + /// + [DllImport("zlib.dll")] + public static extern int unzGetGlobalComment(IntPtr handle, sbyte[] commentBuffer, uint commentBufferLength); + + /// Set the current file of the zip file to the first file. + /// The zip file handle opened by . + /// + /// Zero if there was no error. + /// Otherwise a value less than zero. See for the specific reason. + /// + [DllImport("zlib.dll")] + public static extern int unzGoToFirstFile(IntPtr handle); + + /// Set the current file of the zip file to the next file. + /// The zip file handle opened by . + /// + /// Zero if there was no error. + /// Otherwise if there are no more entries. + /// + [DllImport("zlib.dll")] + public static extern int unzGoToNextFile(IntPtr handle); + + /// Try locate the entry in the zip file. + /// The zip file handle opened by . + /// The name of the entry to look for. + /// If 0 use the OS default. If 1 use case sensitivity like strcmp, Unix style. If 2 do not use case sensitivity like strcmpi, Windows style. + /// + /// Zero if there was no error. + /// Otherwise if there are no more entries. + /// + [DllImport("zlib.dll", ExactSpelling=true, CharSet=CharSet.Ansi)] + public static extern int unzLocateFile(IntPtr handle, string entryName, int caseSensitivity); + + /// Get information about the current entry in the zip file. + /// The zip file handle opened by . + /// A ZipEntryInfo struct to hold information about the entry or null. + /// An array of sbyte characters to hold the entry name or null. + /// The length of the entryNameBuffer in bytes. + /// An array to hold the extra field data for the entry or null. + /// The length of the extraField array in bytes. + /// An array of sbyte characters to hold the entry name or null. + /// The length of theh commentBuffer in bytes. + /// + /// If entryInfoPtr is not null the structure will contain information about the current file. + /// If entryNameBuffer is not null the name of the entry will be copied into it. + /// If extraField is not null the extra field data of the entry will be copied into it. + /// If commentBuffer is not null the comment of the entry will be copied into it. + /// + /// + /// Zero if there was no error. + /// Otherwise a value less than zero. See for the specific reason. + /// + [DllImport("zlib.dll", ExactSpelling=true, CharSet=CharSet.Ansi)] + public unsafe static extern int unzGetCurrentFileInfo(IntPtr handle, ZipEntryInfo* entryInfoPtr, + sbyte[] entryNameBuffer, uint entryNameBufferLength, + byte[] extraField, uint extraFieldLength, + sbyte[] commentBuffer, uint commentBufferLength); + + /// Open the zip file entry for reading. + /// The zip file handle opened by . + /// + /// Zero if there was no error. + /// Otherwise a value from . + /// + [DllImport("zlib.dll")] + public static extern int unzOpenCurrentFile(IntPtr handle); + + /// Close the file entry opened by . + /// The zip file handle opened by . + /// + /// Zero if there was no error. + /// CrcError if the file was read but the Crc does not match. + /// Otherwise a value from . + /// + [DllImport("zlib.dll")] + public static extern int unzCloseCurrentFile(IntPtr handle); + + /// Read bytes from the current zip file entry. + /// The zip file handle opened by . + /// Buffer to store the uncompressed data into. + /// Number of bytes to write from . + /// + /// The number of byte copied if somes bytes are copied. + /// Zero if the end of file was reached. + /// Less than zero with error code if there is an error. See for a list of possible error codes. + /// + [DllImport("zlib.dll")] + public static extern int unzReadCurrentFile(IntPtr handle, byte[] buffer, uint count); + + /// Give the current position in uncompressed data of the zip file entry currently opened. + /// The zip file handle opened by . + /// The number of bytes into the uncompressed data read so far. + [DllImport("zlib.dll")] + public static extern long unztell(IntPtr handle); + + /// Determine if the end of the zip file entry has been reached. + /// The zip file handle opened by . + /// + /// One if the end of file was reached. + /// Zero if elsewhere. + /// + [DllImport("zlib.dll")] + public static extern int unzeof(IntPtr handle); + + /// Converts a CLR string to a 8 bit ANSI array of characters. + /// The string to convert. + /// A 8 bit ANSI array of characters. + public static sbyte[] StringToAnsi(string str) { + int length = str.Length; + sbyte[] chars = new sbyte[length + 1]; + for (int i = 0; i < length; i++) { + chars[i] = (sbyte) str[i]; + } + + return chars; + } + + /// Converst an 8 bit ANSI C style string to a CLR string. + /// The array of a characters that holds the string. + /// The CLR string representing the characters passed in. + public static string AnsiToString(sbyte[] chars) { + int length = chars.Length; + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < length; i++) { + builder.Append((char) chars[i]); + } + return builder.ToString(); + } + } + + /// List of possible error codes. + internal enum ErrorCode : int { + /// No error. + Ok = 0, + + /// Unknown error. + Error = -1, + + /// Last entry in directory reached. + EndOfListOfFile = -100, + + /// Parameter error. + ParameterError = -102, + + /// Zip file is invalid. + BadZipFile = -103, + + /// Internal program error. + InternalError = -104, + + /// Crc values do not match. + CrcError = -105 + } + + /// Global information about the zip file. + [StructLayout(LayoutKind.Sequential)] + internal struct ZipFileInfo { + /// The number of entries in the directory. + public UInt32 EntryCount; + + /// Length of zip file comment in bytes (8 bit characters). + public UInt32 CommentLength; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct ZipFileEntryInfo { + public ZipDateTimeInfo DateTime; + public UInt32 DosDate; + public UInt32 InternalFileAttributes; // 2 bytes + public UInt32 ExternalFileAttributes; // 4 bytes + } + + /// Custom ZipLib date time structure. + [StructLayout(LayoutKind.Sequential)] + internal struct ZipDateTimeInfo { + /// Seconds after the minute - [0,59] + public UInt32 Seconds; + + /// Minutes after the hour - [0,59] + public UInt32 Minutes; + + /// Hours since midnight - [0,23] + public UInt32 Hours; + + /// Day of the month - [1,31] + public UInt32 Day; + + /// Months since January - [0,11] + public UInt32 Month; + + /// Years - [1980..2044] + public UInt32 Year; + + // implicit conversion from DateTime to ZipDateTimeInfo + public static implicit operator ZipDateTimeInfo(DateTime date) { + ZipDateTimeInfo d; + d.Seconds = (uint) date.Second; + d.Minutes = (uint) date.Minute; + d.Hours = (uint) date.Hour; + d.Day = (uint) date.Day; + d.Month = (uint) date.Month - 1; + d.Year = (uint) date.Year; + return d; + } + } + + /// Information stored in zip file directory about an entry. + [StructLayout(LayoutKind.Sequential)] + internal struct ZipEntryInfo { + // Version made by (2 bytes). + public UInt32 Version; + + /// Version needed to extract (2 bytes). + public UInt32 VersionNeeded; + + /// General purpose bit flag (2 bytes). + public UInt32 Flag; + + /// Compression method (2 bytes). + public UInt32 CompressionMethod; + + /// Last mod file date in Dos fmt (4 bytes). + public UInt32 DosDate; + + /// Crc-32 (4 bytes). + public UInt32 Crc; + + /// Compressed size (4 bytes). + public UInt32 CompressedSize; + + /// Uncompressed size (4 bytes). + public UInt32 UncompressedSize; + + /// Filename length (2 bytes). + public UInt32 FileNameLength; + + /// Extra field length (2 bytes). + public UInt32 ExtraFieldLength; + + /// File comment length (2 bytes). + public UInt32 CommentLength; + + /// Disk number start (2 bytes). + public UInt32 DiskStartNumber; + + /// Internal file attributes (2 bytes). + public UInt32 InternalFileAttributes; + + /// External file attributes (4 bytes). + public UInt32 ExternalFileAttributes; + + /// File modification date of entry. + public ZipDateTimeInfo DateTime; + } +} diff --git a/src/Zip/ZipReader.cs b/src/Zip/ZipReader.cs new file mode 100644 index 0000000..54bba75 --- /dev/null +++ b/src/Zip/ZipReader.cs @@ -0,0 +1,298 @@ +// Copyright (C) 2001 Gerry Shaw +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// +// Gerry Shaw (gerry_shaw@yahoo.com) + +using System; +using System.Collections; +using System.Runtime.Serialization; + +namespace OrganicBit.Zip { + + /// Provides support for reading files in the ZIP file format. Includes support for both compressed and uncompressed entries. + /// This example shows how to view the entries in a ZIP file. + /// + /// public static void View(string zipFileName) { + /// ZipReader reader = new ZipReader(zipFileName); + /// + /// Console.WriteLine("Archive: {0} ({1} files)", zipFileName, reader.Entries.Count); + /// Console.WriteLine(reader.Comment); + /// + /// string format = "{0,8} {1,8} {2,5} {3,10} {4,5} {5}"; + /// Console.WriteLine(format, " Length ", " Size ", "Ratio", " Date ", "Time ", "Name"); + /// Console.WriteLine(format, "--------", "--------", "-----", "----------", "-----", "----"); + /// + /// foreach (ZipEntry entry in reader.Entries) { + /// if (!entry.IsDirectory) { + /// Console.WriteLine(format, + /// entry.Length, + /// entry.CompressedLength, + /// entry.Ratio.ToString("P0"), + /// entry.ModifiedTime.ToString("yyyy-MM-dd"), + /// entry.ModifiedTime.ToString("hh:mm"), + /// entry.Name); + /// } + /// } + /// reader.Close(); + /// } + /// + /// + /// This example shows how to extract files from a ZIP file. + /// + /// public static void Extract(string zipFileName) { + /// ZipReader reader = new ZipReader(zipFileName); + /// Console.WriteLine("Archive: {0}", zipFileName); + /// Console.WriteLine(reader.Comment); + /// + /// // buffer to hold temp bytes + /// byte[] buffer = new byte[4096]; + /// int byteCount; + /// + /// // Get the zipped entries + /// while (reader.MoveNext()) { + /// ZipEntry entry = reader.Current; + /// + /// if (entry.IsDirectory) { + /// Directory.CreateDirectory(entry.Name); + /// } else { + /// Console.Write(" {0}", entry.Name); + /// + /// // create output stream + /// FileStream writer = File.Open(entry.Name, FileMode.Create); + /// + /// // write uncompressed data + /// while ((byteCount = reader.Read(buffer, 0, buffer.Length)) > 0) { + /// Console.Write("."); + /// writer.Write(buffer, 0, byteCount); + /// } + /// writer.Close(); + /// Console.WriteLine(); + /// } + /// } + /// reader.Close(); + /// } + /// + /// + public class ZipReader : IEnumerator, IDisposable { + + /// ZipFile handle to read data from. + IntPtr _handle = IntPtr.Zero; + + /// Name of zip file. + string _fileName = null; + + /// Contents of zip file directory. + ZipEntryCollection _entries = null; + + /// Global zip file comment. + string _comment = null; + + /// True if an entry is open for reading. + bool _entryOpen = false; + + /// Current zip entry open for reading. + ZipEntry _current = null; + + /// Initializes a instance of the class for reading the zip file with the given name. + /// The name of zip file that will be read. + public ZipReader(string fileName) { + _fileName = fileName; + _handle = ZipLib.unzOpen(fileName); + if (_handle == IntPtr.Zero) { + string msg = String.Format("Could not open zip file '{0}'.", fileName); + throw new ZipException(msg); + } + } + + /// Cleans up the resources used by this zip file. + ~ZipReader() { + CloseFile(); + } + + /// Dispose is synonym for Close. + void IDisposable.Dispose() { + Close(); + } + + /// Closes the zip file and releases any resources. + public void Close() { + // Free unmanaged resources. + CloseFile(); + + // If base type implements IDisposable we would call it here. + + // Request the system not call the finalizer method for this object. + GC.SuppressFinalize(this); + } + + /// Gets the name of the zip file that was passed to the constructor. + public string Name { + get { return _fileName; } + } + + /// Gets the global comment for the zip file. + public string Comment { + get { + if (_comment == null) { + ZipFileInfo info; + int result = 0; + unsafe { + result = ZipLib.unzGetGlobalInfo(_handle, &info); + } + if (result < 0) { + string msg = String.Format("Could not read comment from zip file '{0}'.", Name); + throw new ZipException(msg); + } + + sbyte[] buffer = new sbyte[info.CommentLength]; + result = ZipLib.unzGetGlobalComment(_handle, buffer, (uint) buffer.Length); + if (result < 0) { + string msg = String.Format("Could not read comment from zip file '{0}'.", Name); + throw new ZipException(msg); + } + _comment = ZipLib.AnsiToString(buffer); + } + return _comment; + } + } + + /// Gets a object that contains all the entries in the zip file directory. + public ZipEntryCollection Entries { + get { + if (_entries == null) { + _entries = new ZipEntryCollection(); + + int result = ZipLib.unzGoToFirstFile(_handle); + while (result == 0) { + ZipEntry entry = new ZipEntry(_handle); + _entries.Add(entry); + result = ZipLib.unzGoToNextFile(_handle); + } + } + return _entries; + } + } + + object IEnumerator.Current { + get { + return _current; + } + } + + /// Gets the current entry in the zip file.. + public ZipEntry Current { + get { + return _current; + } + } + + /// Advances the enumerator to the next element of the collection. + /// Sets to the next zip entry. + /// true if the next entry is not null; otherwise false. + public bool MoveNext() { + // close any open entry + CloseEntry(); + + int result; + if (_current == null) { + result = ZipLib.unzGoToFirstFile(_handle); + } else { + result = ZipLib.unzGoToNextFile(_handle); + } + if (result < 0) { + // last entry found - not an exceptional case + _current = null; + } else { + // entry found + OpenEntry(); + } + + return (_current != null); + } + + /// Move to just before the first entry in the zip directory. + public void Reset() { + CloseEntry(); + _current = null; + } + + /// Seek to the specified entry. + /// The name of the entry to seek to. + public void Seek(string entryName) { + CloseEntry(); + int result = ZipLib.unzLocateFile(_handle, entryName, 0); + if (result < 0) { + string msg = String.Format("Could not locate entry named '{0}'.", entryName); + throw new ZipException(msg); + } + OpenEntry(); + } + + private void OpenEntry() { + _current = new ZipEntry(_handle); + int result = ZipLib.unzOpenCurrentFile(_handle); + if (result < 0) { + _current = null; + throw new ZipException("Could not open entry for reading."); + } + _entryOpen = true; + } + + /// Uncompress a block of bytes from the current zip entry and writes the data in a given buffer. + /// The array to write data into. + /// The byte offset in at which to begin writing. + /// The maximum number of bytes to read. + public int Read(byte[] buffer, int index, int count) { + if (index != 0) { + throw new ArgumentException("index", "Only index values of zero currently supported."); + } + int bytesRead = ZipLib.unzReadCurrentFile(_handle, buffer, (uint) count); + if (bytesRead < 0) { + throw new ZipException("Error reading zip entry."); + } + return bytesRead; + } + + private void CloseEntry() { + if (_entryOpen) { + int result = ZipLib.unzCloseCurrentFile(_handle); + if (result < 0) { + switch ((ErrorCode) result) { + case ErrorCode.CrcError: + throw new ZipException("All the file was read but the CRC did not match."); + + default: + throw new ZipException("Could not close zip entry."); + } + } + _entryOpen = false; + } + } + + private void CloseFile() { + if (_handle != IntPtr.Zero) { + CloseEntry(); + int result = ZipLib.unzClose(_handle); + if (result < 0) { + throw new ZipException("Could not close zip file."); + } + _handle = IntPtr.Zero; + } + } + } +} diff --git a/src/Zip/ZipWriter.cs b/src/Zip/ZipWriter.cs new file mode 100644 index 0000000..0c3e4d5 --- /dev/null +++ b/src/Zip/ZipWriter.cs @@ -0,0 +1,181 @@ +// Copyright (C) 2001 Gerry Shaw +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// +// Gerry Shaw (gerry_shaw@yahoo.com) + +using System; +using System.Runtime.Serialization; + +namespace OrganicBit.Zip { + + /// Provides support for writing files in the ZIP file format. Includes support for both compressed and uncompressed entries. + /// This example shows how to create a ZIP file. + /// + /// public static void Add(string zipFileName, string[] entryPatterns) { + /// string currentDirectory = Directory.GetCurrentDirectory(); + /// Console.WriteLine("Creating {0}", zipFileName); + /// + /// ZipWriter writer = new ZipWriter(zipFileName); + /// + /// // buffer to hold temp bytes + /// byte[] buffer = new byte[4096]; + /// int byteCount; + /// + /// // add files to archive + /// foreach (string pattern in entryPatterns) { + /// foreach (string path in Directory.GetFiles(currentDirectory, pattern)) { + /// string fileName = Path.GetFileName(path); + /// Console.Write("Adding {0}", fileName); + /// + /// ZipEntry entry = new ZipEntry(fileName); + /// entry.ModifiedTime = File.GetLastWriteTime(fileName); + /// entry.Comment = "local file comment"; + /// + /// writer.AddEntry(entry); + /// + /// FileStream reader = File.OpenRead(entry.Name); + /// while ((byteCount = reader.Read(buffer, 0, buffer.Length)) > 0) { + /// Console.Write("."); + /// writer.Write(buffer, 0, byteCount); + /// } + /// reader.Close(); + /// Console.WriteLine(); + /// } + /// } + /// + /// writer.Close(); + /// } + /// + /// + public class ZipWriter : IDisposable { + + /// Name of the zip file. + string _fileName; + + /// Zip file global comment. + string _comment = ""; + + /// True if currently writing a new zip file entry. + bool _entryOpen = false; + + /// Zip file handle. + IntPtr _handle = IntPtr.Zero; + + /// Initializes a new instance fo the class with a specified file name. Any Existing file will be overwritten. + /// The name of the zip file to create. + public ZipWriter(string fileName) { + _fileName = fileName; + + _handle = ZipLib.zipOpen(fileName, 0); + if (_handle == IntPtr.Zero) { + string msg = String.Format("Could not open zip file '{0}' for writing.", fileName); + throw new ZipException(msg); + } + } + + /// Cleans up the resources used by this zip file. + ~ZipWriter() { + CloseFile(); + } + + /// Dispose is synonym for Close. + void IDisposable.Dispose() { + Close(); + } + + /// Closes the zip file and releases any resources. + public void Close() { + // Free unmanaged resources. + CloseFile(); + + // If base type implements IDisposable we would call it here. + + // Request the system not call the finalizer method for this object. + GC.SuppressFinalize(this); + } + + /// Gets the name of the zip file. + public string Name { + get { + return _fileName; + } + } + + /// Gets and sets the zip file comment. + public string Comment { + get { return _comment; } + set { _comment = value; } + } + + /// Creates a new zip entry in the directory and positions the stream to the start of the entry data. + /// The zip entry to be written. + /// Closes the current entry if still active. + public void AddEntry(ZipEntry entry) { + ZipFileEntryInfo info; + info.DateTime = entry.ModifiedTime; + + int result; + unsafe { + byte[] extra = null; + uint extraLength = 0; + if (entry.ExtraField != null) { + extra = entry.ExtraField; + extraLength = (uint) entry.ExtraField.Length; + } + + result = ZipLib.zipOpenNewFileInZip( + _handle, + entry.Name, + &info, + extra, + extraLength, + null, 0, + entry.Comment, + (int) entry.Method, + entry.Level); + } + _entryOpen = true; + } + + /// Compress a block of bytes from the given buffer and writes them into the current zip entry. + /// The array to read data from. + /// The byte offset in at which to begin reading. + /// The maximum number of bytes to write. + public void Write(byte[] buffer, int index, int count) { + int result = ZipLib.zipWriteInFileInZip(_handle, buffer, (uint) count); + } + + private void CloseEntry() { + if (_entryOpen) { + int result = ZipLib.zipCloseFileInZip(_handle); + _entryOpen = false; + } + } + + void CloseFile() { + if (_handle != IntPtr.Zero) { + CloseEntry(); + int result = ZipLib.zipClose(_handle, _comment); + if (result < 0) { + throw new ZipException("Could not close zip file."); + } + _handle = IntPtr.Zero; + } + } + } +}