About TT's Zip Archive Package

Version: 2.0.4 (26 Feb 2010)

This is a collection of REALbasic Classes to extract and create ZIP archives (known as PKZIP and Info-ZIP formats), for Mac OS X, Windows and Linux.

Written and improved since March 2003 by Thomas Tempelmann

For updates see http://www.tempel.org/RB/ZipPackage
For feedback and questions, write to: tt@tempel.org

This code is free for your own use, but you are encouraged to send me some money if you have a use for it (consider it shareware). See the end of this document for more information.

Current requirements: REALbasic 2008 Release 4 or later. The Einhugur e-CryptIt plugin is now optional, and must be acquired separately. Alternatively, and only for Windows, the "zlib" DLL needs to be acquired, e.g. from http://www.zlib.net/

Features

Restrictions

It implements only a subset of the entire ZIP archive definition, though:

The only thing to be sure about is that files created by this class can be read by any modern ZIP tool, such as ZipIt (for Mac OS), OS X's Archives, WinZIP (for Windows), Stuffit Expander (Mac and Windows) and, of course, by itself.

Programming information

To actually be able to create and read compressed items in and archive you will also need a plugin providing the so-called "ZLib" compression functions. You have two choices:

Quick Demo

If you are using Windows, first download the "zlib compiled DLL" from http://www.zlib.net/ and place the contained file zlib1.dll either next to "TT's ZipArchiver.rbp" (only works with RB 2009r2.1 and earlier due to a change in REALbasic 2009r3) or copy it into C:\WINDOWS\system\. If you are using Mac OS X or Linux, you do not need to do this because there is already a zlib library installed on those systems.

Open the project called "TT's ZipArchiver.rbp" and run it. The program should open a window into which you can drop files to compress or decompress instantly. It also allows you to choose options such as the encoding for the file names (unfortunately, different tools on the various computers use different encodings - though this will not be of relevance as long as you're using files names that contain only ASCII characters. Once you use accented chars, symbols or even non-Latin scripts in your file names, you need to make sure that the encoding used for compression matches the one used to later read the archive again.

Overview of the classes

The only classes you need to look at are:

Or, if you only want to uncompress .zip files easily, look at:

The files inside the ZipSupport folder are not of much interest to you, while the files in the StreamSupport folder may be of general use for you in other projects, as they provide generalized read and write functions for both data and resource forks.

There is also a folder called "AddWhenNotUsingEinhugurPlugin". As its name suggests, you need to add its contents to your project if you do not have those plugins installed (you then also need to change the value of the constant "HaveEinhugurPlugin" to false and make sure you have the "zlib1.dll" available if you're running on Windows.

Creating an archive and adding files to it

First, create a new ZipArchive instance. Then you Open the archive by specifying a FolderItem along with a Boolean that is TRUE to signal that you want to write to the archive. If the FolderItem exists, it will be opened as an archive (provided it's a valid archive file), and if no file exists yet, a new archive file will be created.

 dim zar as ZipArchive

 zar = new ZipArchive
 if not zar.Open(aZipFolderItem, true) then
   MsgBox "Error: " + zar.ErrorMessage
   return
 end

Next, you can either add single files or entire folders to the archive:

 dim result as Integer
 
 // add a single file or folder:
 result = zar.AddItemToRoot(aFolderItem, zar.MacBinaryNever)
 if result <= 0 then
   MsgBox "Error: " + zar.ErrorMessage
   return
 end

 // add the contents of a folder:
 if not zar.AddFolderContents(aFolderItem, aFolderItem.Name, zar.MacBinaryNever, ZipArchive.MacAliasHandling.DropAll, false) then
   MsgBox "Error: " + zar.ErrorMessage
   return
 end

Finally, close the archive:

 if not zar.Close() then
   MsgBox "Error: " + zar.ErrorMessage
   return
 end

MacBinary information

The MacBinary option allows you to preserve Macintosh-specific file information, mainly that's the resource forks (it does also preserve some minor attributes such as the File Creation Date, because standard Zip format only preserves the Modification Date).

A Mac file's Type and Creator codes will always be preserved, even if no MacBinary is used. You may want to pass MacBinarySmart or MacBinaryAlways instead of MacBinaryNever in order to better preserve Macintosh specific information (when running on Windows, they make no difference).

The "Smart" version will encode files only as MacBinary if the file contains a Resource Fork.

Note that many non-Macintosh Zip tools can not handle MacBinary encoding, which means that they would decode such files as a MacBinary file, hiding the data fork in them along with other information (some tools can decode this properly, though, like Stuffit Expander for Windows - they extract the data fork only, ignoring the resource fork).

Extracting all items from an archive

Create a new ZipArchive instance, then call Open with passing a FolderItem identifying the archive you want to extract from, and FALSE for reading from the archive (instead of TRUE for writing to it).

 dim zar as ZipArchive

 zar = new ZipArchive
 if not zar.Open(theFile, false) then
   MsgBox "Error: " + zar.ErrorMessage
   return
 end

You can now loop over all entries in the archive, get their "ZipEntry" instances, and then extract their files to a folder hierarchy of which you can provide the starting folder:

 dim f as FolderItem, e as ZipEntry, i as Integer
 
 for i = 1 to zar.EntryCount
   e = zar.Entry(i)
   f = e.MakeDestination(destFolder,false)
   if f.exists then
     // here you could ask the user whether to overwrite the
     // existing file (but it could be even a folder!) or
     // to skip the item or abort the entire process.
     f.Delete // this will not work if it's a non-empty folder, though!
   end
   if not e.Extract(f) then
     MsgBox "Extraction of """+e.RawPath+""" failed: "+e.ErrorMessage
     return
   end
 next

There is a little caveat here, though: Some operating systems (e.g. OS X) update a folder's modification time to the current time when a new file is added to the folder. While a zip archive usually contains entries for folders and their original timestamps, those usually come first, i.e. before their contents. Hence, even if the above loop extracts a folder entry first, settings its original timestamp, successive files extracted into the folder will modify its timestamp again. There is a work-around for this, fortunately: First, extract only the files, and then perform another extraction loop in which only the folders are extracting, thus setting their timestamps after all modifications to the folder are completed. See ZipExtractor.ExtractAllSilently() for an example.

Finally, close the archive again (no need to check for errors since we did only read from the archive):

 call zar.Close()

Hint: To delete a non-empty folder, see here: http://www.declaresub.com/wiki/index.php/Delete_a_non-empty_folder

More detailed programming information

There are many more options to store and retrieve items in a Zip archive:

To learn about all these, look up the functions in the classes (mostly ZipArchive and ZipEntry) and see the comments at the top of them. That's their documentation.

Note that the methods starting with "z_" are private functions, the others are available for general use.

If you find bugs or make enhancements to this software, please contact me about them (send me your changed version), and I'll see to incorporate them into my next release to have others benefit from it, too.

Known limitations and problems

Compatibility & interchangeability considerations

There are many different Zip archiver tools around, and many have interchange problems when it comes to these particularities:

Further reading

The "zip archive" standard is quite a loose one. There appear to be lots of zip programs around that do not follow the PKWARE-specs very well, and while I have tried to program this software as close to the specs as I could, you may run into compatibility problems when trying to open archives from or create archives for other zip implementations.

A universal, open-sourced, reference implementation that attempts to deal with all eventualities is the so-called info-zip code base, in C language, which is also used by Apple with OS X (even though Apple appears to use quite an outdated version of it). Here is the info-zip home page:

Info-Zip home

The original PKWARE specification for Zip archives is included with this package in the "Technical docs" folder, along with the MacBinary specs.

List of changes (Version History)

v1.0, 8 Apr 2003

v1.1, 25 Apr 2003

v1.1.1, 18 June 2003

v1.1.2, 17 July 2003

v1.1.3, 29 July 2004

v.1.2, 2004-2006

v1.3.1, 22 March 2007

2 August 2007

May 2008

v2.0.0, 18 October 2009

v2.0.1, 9 February 2010

v2.0.2, 25 February 2010

v2.0.3, 26 February 2010

v2.0.4, 26 February 2010

Copyrights and Acknowledgements

e-CryptIt Engine copyright Björn Eiríksson (www.einhugur.com)

e-CryptIt Engine uses zlib code, copyright © 1995-2002 Jean-Loup Gailly and Mark Adler.

Original zip format REALbasic code was written by Carsten Friehe for the Mieze program (http://carsten-friehe.de/).

RB code improved and reorganized by Thomas Tempelmann (http://www.tempel.org/rb/) for public release.

Some of the design and error messages was influenced by Java 1.1's ZipFile and related classes.

My thanks go to Leonard Rosenthol (author of Stuffit Zip support and maintainer of MacBinary format) and Tom Brown (author of ZipIt) for providing helpful information.

Terms of use

This RB code, written by Carsten Friehe and Thomas Tempelmann, is given to the Public Domain, which means you can do whatever you want with it. It is, however appreciated if you would "tip" me by sending me a few dollars for my work.

It took me more than two full weeks to develop the original version of this code for the public - I myself could have done with much less for my own needs, but I wanted to provide this as a clean and complete solution so that others won't have to deal with this not-trivial task. And in the past many years I've added a lot of new features as well. So, if you benefit from my work, please acknowledge it with a little financial support for me.

Please visit the following web address to find out how to tip me:

http://tip.tempel.org/

Enjoy!

8 April 2003, 25 February 2010

Thomas Tempelmann