diff --git a/COPYING b/COPYING
new file mode 100644
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+	51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/Codec/Archive/Zip.hs b/Codec/Archive/Zip.hs
new file mode 100644
--- /dev/null
+++ b/Codec/Archive/Zip.hs
@@ -0,0 +1,643 @@
+{-# LANGUAGE CPP #-}
+------------------------------------------------------------------------
+-- |
+-- Module      : Codec.Archive.Zip
+-- Copyright   : John MacFarlane
+-- License     : GPL 2 (see LICENSE)
+--
+-- Maintainer  : John MacFarlane < jgm at berkeley dot edu >
+-- Stability   : unstable
+-- Portability : so far only tested on GHC
+--
+-- The zip-archive library provides functions for creating, modifying,
+-- and extracting files from zip archives.
+--
+-- Certain simplifying assumptions are made about the zip archives: in
+-- particular, there is no support for encryption, zip files that span
+-- multiple disks, ZIP64, OS-specific file attributes, or compression
+-- methods other than Deflate.  However, the library should be able to
+-- read the most common zip archives, and the archives it produces should
+-- be readable by all standard unzip programs.
+--
+-- As an example of the use of the library, a standalone zip archiver
+-- and extracter, Zip.hs, is provided in the source distribution.
+--
+-- For more information on the format of zip archives, consult
+-- <http://www.pkware.com/documents/casestudies/APPNOTE.TXT>
+------------------------------------------------------------------------
+
+module Codec.Archive.Zip
+       (
+
+       -- * Data structures
+         Archive (..)
+       , Entry (..)
+       , CompressionMethod (..)
+       , ZipOption (..)
+       , emptyArchive
+
+       -- * Pure functions for working with zip archives
+       , toArchive
+       , fromArchive
+       , filesInArchive
+       , addEntryToArchive
+       , deleteEntryFromArchive
+       , findEntryByPath
+       , fromEntry
+       , toEntry
+
+       -- * IO functions for working with zip archives
+       , readEntry
+       , writeEntry
+       , addFilesToArchive
+       , extractFilesFromArchive
+
+       ) where
+
+import System.Time ( toUTCTime, addToClockTime, CalendarTime (..), ClockTime (..), TimeDiff (..) )
+import Data.Bits ( shiftL, shiftR, (.&.) )
+import Data.Binary
+import Data.Binary.Get
+import Data.Binary.Put
+import Data.List ( nub, find )
+import Data.Maybe
+import Text.Printf
+import System.FilePath
+import System.Directory ( doesDirectoryExist, getDirectoryContents, createDirectoryIfMissing )
+import qualified Control.Monad.State as S
+import Control.Monad ( when, unless, zipWithM )
+import System.Directory ( getModificationTime )
+import System.IO ( stderr, hPutStrLn )
+import qualified Data.Hash.CRC32.GZip as CRC32
+
+#ifndef _WINDOWS
+import System.Posix.Files ( setFileTimes )
+#endif
+
+-- from bytestring
+import qualified Data.ByteString.Lazy as B
+import Data.ByteString.Internal ( w2c )
+
+-- from utf8-string
+import Data.ByteString.Lazy.UTF8 ( toString, fromString )
+
+-- from zlib
+import qualified Codec.Compression.Zlib.Raw as Zlib
+
+------------------------------------------------------------------------
+
+-- | Structured representation of a zip archive, including directory
+-- information and contents (in lazy bytestrings).
+data Archive = Archive
+                { zEntries                :: [Entry]              -- ^ Files in zip archive
+                , zSignature              :: Maybe B.ByteString   -- ^ Digital signature
+                , zComment                :: B.ByteString         -- ^ Comment for whole zip archive
+                } deriving (Read, Show)
+
+-- | Representation of an archived file, including content and metadata.
+data Entry = Entry
+               { eRelativePath            :: FilePath            -- ^ Relative path, using '/' as separator
+               , eCompressionMethod       :: CompressionMethod   -- ^ Compression method
+               , eLastModified            :: Integer             -- ^ Modification time (seconds since unix epoch)
+               , eCRC32                   :: Word32              -- ^ CRC32 checksum
+               , eCompressedSize          :: Word32              -- ^ Compressed size in bytes
+               , eUncompressedSize        :: Word32              -- ^ Uncompressed size in bytes
+               , eExtraField              :: B.ByteString        -- ^ Extra field - unused by this library
+               , eFileComment             :: B.ByteString        -- ^ File comment - unused by this library
+               , eInternalFileAttributes  :: Word16              -- ^ Internal file attributes - unused by this library
+               , eExternalFileAttributes  :: Word32              -- ^ External file attributes (system-dependent)
+               , eCompressedData          :: B.ByteString        -- ^ Compressed contents of file
+               } deriving (Read, Show, Eq)
+
+-- | Compression methods.
+data CompressionMethod = Deflate
+                       | NoCompression
+                       deriving (Read, Show, Eq)
+
+-- | Options for 'addFilesToArchive' and 'extractFilesFromArchive'.
+data ZipOption = OptRecursive               -- ^ Recurse into directories when adding files
+               | OptVerbose                 -- ^ Print information to stderr
+               deriving (Read, Show, Eq)
+
+-- | A zip archive with no contents.
+emptyArchive :: Archive
+emptyArchive = Archive
+                { zEntries                  = []
+                , zSignature              = Nothing
+                , zComment                = B.empty }
+
+-- | Reads an 'Archive' structure from a raw zip archive (in a lazy bytestring).
+toArchive :: B.ByteString -> Archive
+toArchive = runGet getArchive
+
+-- | Writes an 'Archive' structure to a raw zip archive (in a lazy bytestring).
+fromArchive :: Archive -> B.ByteString
+fromArchive = runPut . putArchive
+
+-- | Returns a list of files in a zip archive.
+filesInArchive :: Archive -> [FilePath]
+filesInArchive = (map eRelativePath) . zEntries
+
+-- | Adds an entry to a zip archive, or updates an existing entry.
+addEntryToArchive :: Entry -> Archive -> Archive
+addEntryToArchive entry archive =
+  let archive'   = deleteEntryFromArchive (eRelativePath entry) archive
+      oldEntries = zEntries archive'
+  in  archive' { zEntries = entry : oldEntries }
+
+-- | Deletes an entry from a zip archive.
+deleteEntryFromArchive :: FilePath -> Archive -> Archive
+deleteEntryFromArchive path archive =
+  let path'      = zipifyFilePath path
+      newEntries = filter (\e -> eRelativePath e /= path') $ zEntries archive
+  in  archive { zEntries = newEntries }
+
+-- | Returns Just the zip entry with the specified path, or Nothing.
+findEntryByPath :: FilePath -> Archive -> Maybe Entry
+findEntryByPath path archive = find (\e -> path == eRelativePath e) (zEntries archive)
+
+-- | Returns uncompressed contents of zip entry.
+fromEntry :: Entry -> B.ByteString
+fromEntry entry =
+  let uncompressedData = decompressData (eCompressionMethod entry) (eCompressedData entry)
+  in  if eCRC32 entry == computeCRC32 uncompressedData
+         then uncompressedData
+         else error "CRC32 mismatch"
+
+-- | Create an 'Entry' with specified file path, modification time, and contents.
+toEntry :: FilePath         -- ^ File path for entry
+        -> Integer          -- ^ Modification time for entry (seconds since unix epoch)
+        -> B.ByteString     -- ^ Contents of entry
+        -> Entry
+toEntry path modtime contents =
+  let uncompressedSize = B.length contents
+      compressedData = compressData Deflate contents
+      compressedSize = B.length compressedData
+      -- only use compression if it helps!
+      (compressionMethod, finalData, finalSize) =
+        if uncompressedSize <= compressedSize
+           then (NoCompression, contents, uncompressedSize)
+           else (Deflate, compressedData, compressedSize)
+      crc32 = CRC32.calc_crc32 $ map w2c $ B.unpack contents
+  in  Entry { eRelativePath            = path
+            , eCompressionMethod       = compressionMethod
+            , eLastModified            = modtime
+            , eCRC32                   = crc32
+            , eCompressedSize          = fromIntegral finalSize
+            , eUncompressedSize        = fromIntegral uncompressedSize
+            , eExtraField              = B.empty
+            , eFileComment             = B.empty
+            , eInternalFileAttributes  = 0  -- potentially non-text
+            , eExternalFileAttributes  = 0  -- appropriate if from stdin
+            , eCompressedData          = finalData
+            }
+
+-- | Generates a 'Entry' from a file or directory.
+readEntry :: [ZipOption] -> FilePath -> IO Entry
+readEntry opts path = do
+  isDir <- doesDirectoryExist path
+  let path' = zipifyFilePath $ normalise $
+              path ++ if isDir then "/" else ""  -- make sure directories end with /
+  contents <- if isDir
+                 then return B.empty
+                 else B.readFile path
+  (TOD modEpochTime _) <- getModificationTime path
+  let entry = toEntry path' modEpochTime contents
+  when (OptVerbose `elem` opts) $ do
+    let compmethod = case eCompressionMethod entry of
+                     Deflate       -> "deflated"
+                     NoCompression -> "stored"
+    hPutStrLn stderr $
+      printf "  adding: %s (%s %.f%%)" (eRelativePath entry)
+      compmethod (100 - (100 * compressionRatio entry))
+  return entry
+
+-- | Writes contents of an 'Entry' to a file.
+writeEntry :: [ZipOption] -> Entry -> IO ()
+writeEntry opts entry = do
+  let path = eRelativePath entry
+  -- create directories if needed
+  let dir = takeDirectory path
+  exists <- doesDirectoryExist dir
+  unless exists $ do
+    createDirectoryIfMissing True dir
+    when (OptVerbose `elem` opts) $
+      hPutStrLn stderr $ "  creating: " ++ dir
+  if length path > 0 && last path == '/' -- path is a directory
+     then return ()
+     else do
+       when (OptVerbose `elem` opts) $ do
+         hPutStrLn stderr $ case eCompressionMethod entry of
+                                 Deflate       -> " inflating: " ++ path
+                                 NoCompression -> "extracting: " ++ path
+       B.writeFile path (fromEntry entry)
+  -- Note that last modified times are supported only for POSIX, not for
+  -- Windows.
+  setFileTimeStamp path (eLastModified entry)
+
+-- | Add the specified files to an 'Archive'.  If 'OptRecursive' is specified,
+-- recursively add files contained in directories.  If 'OptVerbose' is specified,
+-- print messages to stderr.
+addFilesToArchive :: [ZipOption] -> Archive -> [FilePath] -> IO Archive
+addFilesToArchive opts archive files = do
+  filesAndChildren <- if OptRecursive `elem` opts
+                         then mapM getDirectoryContentsRecursive files >>= return . nub . concat
+                         else return files
+  entries <- mapM (readEntry opts) filesAndChildren
+  return $ foldr addEntryToArchive archive entries
+
+-- | Extract all files from an 'Archive', creating directories
+-- as needed.  If 'OptVerbose' is specified, print messages to stderr.
+-- Note that the last-modified time is set correctly only in POSIX,
+-- not in Windows.
+extractFilesFromArchive :: [ZipOption] -> Archive -> IO ()
+extractFilesFromArchive opts archive = mapM_ (writeEntry opts) $ zEntries archive
+
+--------------------------------------------------------------------------------
+-- Internal functions for reading and writing zip binary format.
+
+-- | Perform a sequence of actions until one returns Nothing;
+-- return list of results.
+many :: Monad m => m (Maybe a) -> m [a]
+many p = do
+  r <- p
+  case r of
+       Just x  ->  many p >>= return . (x:)
+       Nothing -> return []
+
+-- Note that even on Windows, zip files use "/" internally as path separator.
+zipifyFilePath :: FilePath -> String
+zipifyFilePath path =
+  let dir = takeDirectory path
+      fn  = takeFileName path
+      dirParts = splitDirectories dir
+  in  concat (map (++ "/") dirParts) ++ fn
+
+computeCRC32 :: B.ByteString -> Word32
+computeCRC32 = CRC32.calc_crc32 . map w2c . B.unpack
+
+-- | Uncompress a lazy bytestring.
+compressData :: CompressionMethod -> B.ByteString -> B.ByteString
+compressData Deflate       = Zlib.compress
+compressData NoCompression = id
+
+-- | Compress a lazy bytestring.
+decompressData :: CompressionMethod -> B.ByteString -> B.ByteString
+decompressData Deflate       = Zlib.decompress
+decompressData NoCompression = id
+
+-- | Calculate compression ratio for an entry (for verbose output).
+compressionRatio :: Entry -> Float
+compressionRatio entry =
+  if eUncompressedSize entry == 0
+     then 1
+     else fromIntegral (eCompressedSize entry) / fromIntegral (eUncompressedSize entry)
+
+-- | MSDOS datetime: a pair of Word16s (date, time) with the following structure:
+--
+-- > DATE bit     0 - 4           5 - 8           9 - 15
+-- >      value   day (1 - 31)    month (1 - 12)  years from 1980
+-- > TIME bit     0 - 4           5 - 10          11 - 15
+-- >      value   seconds*        minute          hour
+-- >              *stored in two-second increments
+--
+data MSDOSDateTime = MSDOSDateTime { msDOSDate :: Word16
+                                   , msDOSTime :: Word16
+                                   } deriving (Read, Show, Eq)
+
+-- | Convert a clock time to a MSDOS datetime.  The MSDOS time will be relative to UTC.
+epochTimeToMSDOSDateTime :: Integer -> MSDOSDateTime
+epochTimeToMSDOSDateTime epochtime =
+  let ut = toUTCTime (TOD epochtime 0)
+      dosTime = toEnum $ (ctSec ut `div` 2) + shiftL (ctMin ut) 5 + shiftL (ctHour ut) 11
+      dosDate = toEnum $ ctDay ut + shiftL (fromEnum (ctMonth ut) + 1) 5 + shiftL (ctYear ut - 1980) 9
+  in  MSDOSDateTime { msDOSDate = dosDate, msDOSTime = dosTime }
+
+-- | Convert a MSDOS datetime to a 'ClockTime'.
+msDOSDateTimeToEpochTime :: MSDOSDateTime -> Integer
+msDOSDateTimeToEpochTime (MSDOSDateTime {msDOSDate = dosDate, msDOSTime = dosTime}) =
+  let seconds = fromIntegral $ 2 * (dosTime .&. 0O37)
+      minutes = fromIntegral $ (shiftR dosTime 5) .&. 0O77
+      hour    = fromIntegral $ shiftR dosTime 11
+      day     = fromIntegral $ dosDate .&. 0O37
+      month   = fromIntegral $ ((shiftR dosDate 5) .&. 0O17) - 1
+      year    = fromIntegral $ shiftR dosDate 9
+      timeSinceEpoch = TimeDiff
+               { tdYear = year + 10, -- dos times since 1980, unix epoch starts 1970
+                 tdMonth = month,
+                 tdDay = day - 1,  -- dos days start from 1
+                 tdHour = hour,
+                 tdMin = minutes,
+                 tdSec = seconds,
+                 tdPicosec = 0 }
+      (TOD epochsecs _) = addToClockTime timeSinceEpoch (TOD 0 0)
+  in  epochsecs
+
+getDirectoryContentsRecursive :: FilePath -> IO [FilePath]
+getDirectoryContentsRecursive path = do
+  isDir <- doesDirectoryExist path
+  if isDir
+     then do
+       contents <- getDirectoryContents path
+       let contents' = map (path </>) $ filter (`notElem` ["..","."]) contents
+       children <- mapM getDirectoryContentsRecursive contents'
+       if path == "."
+          then return (concat children)
+          else return (path : concat children)
+     else return [path]
+
+setFileTimeStamp :: FilePath -> Integer -> IO ()
+setFileTimeStamp file epochtime = do
+#ifdef _WINDOWS
+  return ()  -- TODO - figure out how to set the timestamp on Windows
+#else
+  let epochtime' = fromInteger epochtime
+  setFileTimes file epochtime' epochtime'
+#endif
+
+-- A zip file has the following format (*'d items are not supported in this implementation):
+--
+-- >   [local file header 1]
+-- >   [file data 1]
+-- >   [data descriptor 1*]
+-- >   .
+-- >   .
+-- >   .
+-- >   [local file header n]
+-- >   [file data n]
+-- >   [data descriptor n*]
+-- >   [archive decryption header*]
+-- >   [archive extra data record*]
+-- >   [central directory]
+-- >   [zip64 end of central directory record*]
+-- >   [zip64 end of central directory locator*]
+-- >   [end of central directory record]
+--
+-- Files stored in arbitrary order.  All values are stored in
+-- little-endian byte order unless otherwise specified.
+--
+--  Central directory structure:
+--
+-- >   [file header 1]
+-- >   .
+-- >   .
+-- >   .
+-- >   [file header n]
+-- >   [digital signature]
+--
+--  End of central directory record:
+--
+-- >   end of central dir signature    4 bytes  (0x06054b50)
+-- >   number of this disk             2 bytes
+-- >   number of the disk with the
+-- >   start of the central directory  2 bytes
+-- >   total number of entries in the
+-- >   central directory on this disk  2 bytes
+-- >   total number of entries in
+-- >   the central directory           2 bytes
+-- >   size of the central directory   4 bytes
+-- >   offset of start of central
+-- >   directory with respect to
+-- >   the starting disk number        4 bytes
+-- >   .ZIP file comment length        2 bytes
+-- >   .ZIP file comment       (variable size)
+
+getArchive :: Get Archive
+getArchive = do
+  locals <- many getLocalFile
+  files <- many (getFileHeader locals)
+  digSig <- lookAheadM getDigitalSignature
+  endSig <- getWord32le
+  unless (endSig == 0x06054b50) $ fail "Did not find end of central directory signature"
+  skip 2 -- disk number
+  skip 2 -- disk number of central directory
+  skip 2 -- num entries on this disk
+  skip 2 -- num entries in central directory
+  skip 4 -- central directory size
+  skip 4 -- offset of central directory
+  commentLength <- getWord16le
+  zipComment <- getLazyByteString (toEnum $ fromEnum commentLength)
+  return $ Archive
+           { zEntries                = files
+           , zSignature              = digSig
+           , zComment                = zipComment
+           }
+
+putArchive :: Archive -> Put
+putArchive archive = do
+  mapM_ putLocalFile $ zEntries archive
+  let localFileSizes = map localFileSize $ zEntries archive
+  let offsets = scanl (+) 0 localFileSizes
+  let cdOffset = last offsets
+  zipWithM putFileHeader offsets (zEntries archive)
+  putDigitalSignature $ zSignature archive
+  putWord32le 0x06054b50
+  putWord16le 0 -- disk number
+  putWord16le 0 -- disk number of central directory
+  putWord16le $ fromIntegral $ length $ zEntries archive -- number of entries this disk
+  putWord16le $ fromIntegral $ length $ zEntries archive -- number of entries
+  putWord32le $ sum $ map fileHeaderSize $ zEntries archive  -- size of central directory
+  putWord32le $ fromIntegral cdOffset                    -- offset of central dir
+  putWord16le $ fromIntegral $ B.length $ zComment archive
+  putLazyByteString $ zComment archive
+
+
+fileHeaderSize :: Entry -> Word32
+fileHeaderSize f =
+  fromIntegral $ 4 + 2 + 2 + 2 + 2 + 2 + 2 + 4 + 4 + 4 + 2 + 2 + 2 + 2 + 2 + 4 + 4 +
+    fromIntegral (B.length $ fromString $ zipifyFilePath $ eRelativePath f) +
+    B.length (eExtraField f) + B.length (eFileComment f)
+
+localFileSize :: Entry -> Word32
+localFileSize f =
+  fromIntegral $ 4 + 2 + 2 + 2 + 2 + 2 + 4 + 4 + 4 + 2 + 2 +
+    fromIntegral (B.length $ fromString $ zipifyFilePath $ eRelativePath f) +
+    B.length (eExtraField f) + B.length (eCompressedData f)
+
+-- Local file header:
+--
+-- >    local file header signature     4 bytes  (0x04034b50)
+-- >    version needed to extract       2 bytes
+-- >    general purpose bit flag        2 bytes
+-- >    compression method              2 bytes
+-- >    last mod file time              2 bytes
+-- >    last mod file date              2 bytes
+-- >    crc-32                          4 bytes
+-- >    compressed size                 4 bytes
+-- >    uncompressed size               4 bytes
+-- >    file name length                2 bytes
+-- >    extra field length              2 bytes
+--
+-- >    file name (variable size)
+-- >    extra field (variable size)
+
+getLocalFile :: Get (Maybe (Word32, B.ByteString))
+getLocalFile = do
+  sig <- lookAhead getWord32le
+  if sig /= 0x04034b50
+    then return Nothing
+    else do
+      offset <- bytesRead
+      skip 4  -- signature
+      skip 2  -- version
+      skip 2  -- general purpose bit flag
+      skip 2  -- compressionMethod
+      skip 2  -- last mod file time
+      skip 2  -- last mod file date
+      skip 4  -- crc32
+      compressedSize <- getWord32le
+      when (compressedSize == 0xFFFFFFFF) $
+        fail "Can't read ZIP64 archive."
+      skip 4  -- uncompressedsize
+      fileNameLength <- getWord16le
+      extraFieldLength <- getWord16le
+      skip (fromIntegral fileNameLength)  -- filename
+      skip (fromIntegral extraFieldLength) -- extra field
+      compressedData <- getLazyByteString (fromIntegral compressedSize)
+      return $ Just (fromIntegral offset, compressedData)
+
+putLocalFile :: Entry -> Put
+putLocalFile f = do
+  putWord32le 0x04034b50
+  putWord16le 20 -- version needed to extract (>=2.0)
+  putWord16le 2  -- general purpose bit flag (max compression)
+  putWord16le $ case eCompressionMethod f of
+                     NoCompression -> 0
+                     Deflate       -> 8
+  let modTime = epochTimeToMSDOSDateTime $ eLastModified f
+  putWord16le $ msDOSTime modTime
+  putWord16le $ msDOSDate modTime
+  putWord32le $ eCRC32 f
+  putWord32le $ eCompressedSize f
+  putWord32le $ eUncompressedSize f
+  putWord16le $ fromIntegral $ length $ eRelativePath f
+  putWord16le $ fromIntegral $ B.length $ eExtraField f
+  putLazyByteString $ fromString $ zipifyFilePath $ eRelativePath f
+  putLazyByteString $ eExtraField f
+  putLazyByteString $ eCompressedData f
+
+-- File header structure:
+--
+-- >    central file header signature   4 bytes  (0x02014b50)
+-- >    version made by                 2 bytes
+-- >    version needed to extract       2 bytes
+-- >    general purpose bit flag        2 bytes
+-- >    compression method              2 bytes
+-- >    last mod file time              2 bytes
+-- >    last mod file date              2 bytes
+-- >    crc-32                          4 bytes
+-- >    compressed size                 4 bytes
+-- >    uncompressed size               4 bytes
+-- >    file name 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
+-- >    relative offset of local header 4 bytes
+--
+-- >    file name (variable size)
+-- >    extra field (variable size)
+-- >    file comment (variable size)
+
+getFileHeader :: [(Word32, B.ByteString)]  -- ^ list of (offset, content) pairs returned by getLocalFile
+              -> Get (Maybe Entry)
+getFileHeader locals = do
+  sig <- lookAhead getWord32le
+  if sig /= 0x02014b50
+     then return Nothing
+     else do
+       skip 4 -- skip past signature
+       skip 2 -- version made by
+       versionNeededToExtract <- getWord16le
+       unless (versionNeededToExtract <= 20) $
+         fail "This archive requires zip >= 2.0 to extract."
+       skip 2 -- general purpose bit flag
+       rawCompressionMethod <- getWord16le
+       compressionMethod <- case rawCompressionMethod of
+                             0 -> return NoCompression
+                             8 -> return Deflate
+                             _ -> fail $ "Unknown compression method " ++ show rawCompressionMethod
+       lastModFileTime <- getWord16le
+       lastModFileDate <- getWord16le
+       crc32 <- getWord32le
+       compressedSize <- getWord32le
+       uncompressedSize <- getWord32le
+       fileNameLength <- getWord16le
+       extraFieldLength <- getWord16le
+       fileCommentLength <- getWord16le
+       skip 2 -- disk number start
+       internalFileAttributes <- getWord16le
+       externalFileAttributes <- getWord32le
+       relativeOffset <- getWord32le
+       fileName <- getLazyByteString (toEnum $ fromEnum fileNameLength)
+       extraField <- getLazyByteString (toEnum $ fromEnum extraFieldLength)
+       fileComment <- getLazyByteString (toEnum $ fromEnum fileCommentLength)
+       compressedData <- case lookup relativeOffset locals of
+                         Just x  -> return x
+                         Nothing -> fail $ "Unable to find data at offset " ++ show relativeOffset
+       return $ Just $ Entry
+                 { eRelativePath            = toString fileName
+                 , eCompressionMethod       = compressionMethod
+                 , eLastModified            = msDOSDateTimeToEpochTime $
+                                              MSDOSDateTime { msDOSDate = lastModFileDate,
+                                                              msDOSTime = lastModFileTime }
+                 , eCRC32                   = crc32
+                 , eCompressedSize          = compressedSize
+                 , eUncompressedSize        = uncompressedSize
+                 , eExtraField              = extraField
+                 , eFileComment             = fileComment
+                 , eInternalFileAttributes  = internalFileAttributes
+                 , eExternalFileAttributes  = externalFileAttributes
+                 , eCompressedData          = compressedData
+                 }
+
+putFileHeader :: Word32        -- ^ offset
+              -> Entry
+              -> Put
+putFileHeader offset local = do
+  putWord32le 0x02014b50
+  putWord16le 0  -- version made by
+  putWord16le 20 -- version needed to extract (>= 2.0)
+  putWord16le 2  -- general purpose bit flag (max compression)
+  putWord16le $ case eCompressionMethod local of
+                     NoCompression -> 0
+                     Deflate       -> 8
+  let modTime = epochTimeToMSDOSDateTime $ eLastModified local
+  putWord16le $ msDOSTime modTime
+  putWord16le $ msDOSDate modTime
+  putWord32le $ eCRC32 local
+  putWord32le $ eCompressedSize local
+  putWord32le $ eUncompressedSize local
+  putWord16le $ fromIntegral $ length $ eRelativePath local
+  putWord16le $ fromIntegral $ B.length $ eExtraField local
+  putWord16le $ fromIntegral $ B.length $ eFileComment local
+  putWord16le 0  -- disk number start
+  putWord16le $ eInternalFileAttributes local
+  putWord32le $ eExternalFileAttributes local
+  putWord32le offset
+  putLazyByteString $ fromString $ zipifyFilePath $ eRelativePath local
+  putLazyByteString $ eExtraField local
+  putLazyByteString $ eFileComment local
+
+--  Digital signature:
+--
+-- >     header signature                4 bytes  (0x05054b50)
+-- >     size of data                    2 bytes
+-- >     signature data (variable size)
+
+getDigitalSignature :: Get (Maybe B.ByteString)
+getDigitalSignature = do
+  hdrSig <- getWord32le
+  if hdrSig /= 0x08064b50
+     then return Nothing
+     else do
+        sigSize <- getWord16le
+        getLazyByteString (toEnum $ fromEnum sigSize) >>= return . Just
+
+putDigitalSignature :: Maybe B.ByteString -> Put
+putDigitalSignature Nothing = return ()
+putDigitalSignature (Just sig) = do
+  putWord32le 0x08064b50
+  putWord16le $ fromIntegral $ B.length sig
+  putLazyByteString sig
diff --git a/Data/Hash/CRC32/GZip.hs b/Data/Hash/CRC32/GZip.hs
new file mode 100644
--- /dev/null
+++ b/Data/Hash/CRC32/GZip.hs
@@ -0,0 +1,111 @@
+{- arch-tag: GZIP CRC32 implementation in pure Haskell
+Copyright (C) 2004 John Goerzen <jgoerzen@complete.org>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+-}
+
+{- |
+   Module     : Data.Hash.CRC32.GZip
+   Copyright  : Copyright (C) 2004 John Goerzen
+   License    : GNU GPL, version 2 or later
+
+   Maintainer : John Goerzen <jgoerzen@complete.org> 
+   Stability  : provisional
+   Portability: portable
+
+CRC32 checksumming using the GZIP\/PKZIP algorithm as used in both
+ISO 3309 and section 8.1.1.6.2 of ITU-T V.42 and referenced in 
+RFC1952.
+
+Copyright (c) 2004 John Goerzen, jgoerzen\@complete.org
+-}
+
+module Data.Hash.CRC32.GZip where
+
+import Data.Array
+import Data.Bits
+import Data.Word
+import Data.List
+import Data.Char
+
+update_crc :: Word32 -> Char -> Word32
+update_crc crc ch =
+    let c = crc `xor` 0xFFFFFFFF
+        newval = (gzipcrctab ! fromIntegral ((c `xor` fromIntegral (ord ch)) .&. 0xff))
+                 `xor` (c `shiftR` 8)
+        in
+        newval `xor` 0xFFFFFFFF
+
+update_crc_list :: Word32 -> [Char] -> Word32
+update_crc_list start list = foldl update_crc start list
+
+calc_crc32 :: [Char] -> Word32
+calc_crc32 s = update_crc_list 0 s
+        
+gzipcrctab :: Array Int Word32
+gzipcrctab = array (0,255) (zip [0..255]
+  [
+  0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
+  0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
+  0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
+  0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+  0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
+  0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+  0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
+  0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+  0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
+  0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
+  0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
+  0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+  0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
+  0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
+  0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
+  0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+  0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
+  0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+  0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
+  0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+  0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
+  0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
+  0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
+  0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+  0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
+  0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
+  0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
+  0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+  0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
+  0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+  0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
+  0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+  0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
+  0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
+  0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
+  0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+  0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
+  0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
+  0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
+  0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+  0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
+  0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+  0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
+  0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+  0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
+  0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
+  0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
+  0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+  0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
+  0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
+  0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
+  0x2d02ef8d ])
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,28 @@
+zip-archive - Haskell library
+Copyright (c) 2008 John MacFarlane (jgm@berkeley.edu)
+
+All code is under the following license unless otherwise noted:
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+   A copy of the license can be found in the file COPYING in the
+   source distribution.   
+
+-----------------------------------------------------------------------------
+
+Data.Hash.CRC32.GZip is included from MissingH, to avoid inheriting
+MissingH's many dependencies. 
+Copyright (C) 2004-2006 John Goerzen <jgoerzen@complete.org>
+Released under the GPL as above.
+
diff --git a/Setup.lhs b/Setup.lhs
new file mode 100644
--- /dev/null
+++ b/Setup.lhs
@@ -0,0 +1,3 @@
+#!/usr/bin/env runhaskell
+> import Distribution.Simple
+> main = defaultMain
diff --git a/Tests.hs b/Tests.hs
new file mode 100644
--- /dev/null
+++ b/Tests.hs
@@ -0,0 +1,92 @@
+-- Test suite for Codec.Archive.Zip
+-- runghc Test.hs
+
+import Codec.Archive.Zip
+import System.Directory
+import Test.HUnit.Base
+import Test.HUnit.Text
+import System.Time
+import System.Process
+import qualified Data.ByteString.Lazy as B
+import Control.Applicative
+
+-- define equality for Archives so timestamps aren't distinguished if they
+-- correspond to the same MSDOS datetime.
+instance Eq Archive where
+  (==) a1 a2 =  zSignature a1 == zSignature a2
+             && zComment a1 == zComment a2
+             && (all id $ zipWith (\x y -> x { eLastModified = eLastModified x `div` 2  } ==
+                                           y { eLastModified = eLastModified y `div` 2  }) (zEntries a1) (zEntries a2))
+
+main :: IO Counts
+main = do
+  createDirectory "test-temp"
+  counts <- runTestTT $ TestList [ 
+                                   testReadWriteArchive
+                                 , testReadExternalZip
+                                 , testFromToArchive
+                                 , testReadWriteEntry
+                                 , testAddFilesOptions
+                                 , testDeleteEntries
+                                 , testExtractFiles
+                                 ]
+  removeDirectoryRecursive "test-temp"
+  return counts
+
+testReadWriteArchive = TestCase $ do
+  archive <- addFilesToArchive [OptRecursive] emptyArchive ["LICENSE", "Codec"]
+  B.writeFile "test-temp/test1.zip" $ fromArchive archive
+  archive' <- toArchive <$> B.readFile "test-temp/test1.zip"
+  assertEqual "for writing and reading test1.zip" archive archive'
+  assertEqual "for writing and reading test1.zip" archive archive'
+
+testReadExternalZip = TestCase $ do
+  runCommand "zip -q -9 test-temp/test4.zip zip-archive.cabal Codec/Archive/Zip.hs" >>= waitForProcess
+  archive <- toArchive <$> B.readFile "test-temp/test4.zip"
+  let files = filesInArchive archive
+  assertEqual "for results of filesInArchive" ["zip-archive.cabal", "Codec/Archive/Zip.hs"] files
+  cabalContents <- B.readFile "zip-archive.cabal"
+  case findEntryByPath "zip-archive.cabal" archive of 
+       Nothing  -> assertFailure "zip-archive.cabal not found in archive"
+       Just f   -> assertEqual "for contents of zip-archive.cabal in archive" cabalContents (fromEntry f)
+
+testFromToArchive = TestCase $ do
+  archive <- addFilesToArchive [OptRecursive] emptyArchive ["LICENSE", "Codec"]
+  assertEqual "for (toArchive $ fromArchive archive)" archive (toArchive $ fromArchive archive)
+
+testReadWriteEntry = TestCase $ do
+  entry <- readEntry [] "zip-archive.cabal"
+  setCurrentDirectory "test-temp"
+  writeEntry [] entry
+  setCurrentDirectory ".."
+  entry' <- readEntry [] "test-temp/zip-archive.cabal"
+  let entry'' = entry' { eRelativePath = eRelativePath entry, eLastModified = eLastModified entry }
+  assertEqual "for readEntry -> writeEntry -> readEntry" entry entry''
+
+testAddFilesOptions = TestCase $ do
+  archive1 <- addFilesToArchive [OptVerbose] emptyArchive ["LICENSE", "Codec"]
+  archive2 <- addFilesToArchive [OptRecursive, OptVerbose] archive1 ["LICENSE", "Codec"]
+  assertBool "for recursive and nonrecursive addFilesToArchive"
+     (length (filesInArchive archive1) < length (filesInArchive archive2))
+
+testDeleteEntries = TestCase $ do
+  archive1 <- addFilesToArchive [] emptyArchive ["LICENSE", "Codec/"] 
+  let archive2 = deleteEntryFromArchive "LICENSE" archive1
+  let archive3 = deleteEntryFromArchive "Codec/" archive2
+  assertEqual "for deleteFilesFromArchive" emptyArchive archive3
+
+testExtractFiles = TestCase $ do
+  createDirectory "test-temp/dir1"
+  createDirectory "test-temp/dir1/dir2"
+  let hiMsg = "hello there"
+  let helloMsg = "Hello there. This file is very long.  Longer than 31 characters."
+  writeFile "test-temp/dir1/hi" hiMsg
+  writeFile "test-temp/dir1/dir2/hello" helloMsg
+  archive <- addFilesToArchive [OptRecursive] emptyArchive ["test-temp/dir1"]
+  removeDirectoryRecursive "test-temp/dir1"
+  extractFilesFromArchive [OptVerbose] archive
+  hi <- readFile "test-temp/dir1/hi"
+  hello <- readFile "test-temp/dir1/dir2/hello"
+  assertEqual "contents of test-temp/dir1/hi" hiMsg hi
+  assertEqual "contents of test-temp/dir1/dir2/hello" helloMsg hello
+
diff --git a/Zip.hs b/Zip.hs
new file mode 100644
--- /dev/null
+++ b/Zip.hs
@@ -0,0 +1,80 @@
+------------------------------------------------------------------------
+-- Zip.hs
+-- Copyright (c) 2008 John MacFarlane
+-- License     : GPL 2 (see LICENSE)
+--
+-- This is a demonstration of the use of the 'Codec.Archive.Zip' library.
+-- It duplicates some of the functionality of the 'zip' command-line
+-- program.
+------------------------------------------------------------------------
+
+import Codec.Archive.Zip
+import System.IO
+import qualified Data.ByteString.Lazy as B
+import System.Exit
+import System.Environment
+import System.Directory
+import System.Console.GetOpt
+import Control.Monad ( when )
+import Control.Applicative ( (<$>) )
+
+data Flag 
+  = Quiet 
+  | Version
+  | Decompress
+  | Recursive
+  | Remove
+  | List
+  | Help
+  deriving (Eq, Show, Read)
+
+options :: [OptDescr Flag]
+options =
+   [ Option ['d']   ["decompress"] (NoArg Decompress)    "decompress (unzip)"
+   , Option ['r']   ["recursive"]  (NoArg Recursive)     "recursive"
+   , Option ['R']   ["remove"]     (NoArg Remove)        "remove"
+   , Option ['l']   ["list"]       (NoArg List)          "list"
+   , Option ['v']   ["version"]    (NoArg Version)       "version"
+   , Option ['q']   ["quiet"]      (NoArg Quiet)         "quiet"
+   , Option ['h']   ["help"]       (NoArg Help)          "help"
+   ]
+
+main :: IO ()
+main = do
+  argv <- getArgs
+  progname <- getProgName
+  let header = "Usage: " ++ progname ++ " [OPTION...] archive files..."
+  (opts, args) <- case getOpt Permute options argv of
+      (o, _, _)      | Version `elem` o -> putStrLn "version 0.1" >> exitWith ExitSuccess
+      (o, _, _)      | Help `elem` o    -> error $ usageInfo header options
+      (o, (a:as), [])                   -> return (o, a:as)
+      (_, _, errs)                      -> error $ concat errs ++ "\n" ++ usageInfo header options
+  let verbosity = if Quiet `elem` opts then [] else [OptVerbose]
+  let cmd = take 1 $ filter (`notElem` [Quiet, Help, Version]) opts
+  let cmd' = if null cmd
+                then Recursive
+                else head cmd
+  let (archivePath : files) = args
+  exists <- doesFileExist archivePath
+  archive <- if exists
+                then toArchive <$> B.readFile archivePath
+                else return emptyArchive
+  case cmd' of
+       Decompress  -> extractFilesFromArchive verbosity archive  
+       Remove      -> do tempDir <- getTemporaryDirectory
+                         (tempArchivePath, tempArchive) <- openTempFile tempDir "zip" 
+                         B.hPut tempArchive $ fromArchive $ 
+                                              foldr deleteEntryFromArchive archive files
+                         hClose tempArchive
+                         copyFile tempArchivePath archivePath
+                         removeFile tempArchivePath
+       List        -> mapM_ putStrLn $ filesInArchive archive
+       Recursive   -> do when (null files) $ error "No files specified."
+                         tempDir <- getTemporaryDirectory
+                         (tempArchivePath, tempArchive) <- openTempFile tempDir "zip" 
+                         addFilesToArchive (verbosity ++ [OptRecursive]) archive files >>= 
+                            B.hPut tempArchive . fromArchive
+                         hClose tempArchive
+                         copyFile tempArchivePath archivePath
+                         removeFile tempArchivePath
+       _           -> error "Unknown command"
diff --git a/zip-archive.cabal b/zip-archive.cabal
new file mode 100644
--- /dev/null
+++ b/zip-archive.cabal
@@ -0,0 +1,44 @@
+Name:                zip-archive
+Version:             0.1
+Cabal-version:       >= 1.2
+Build-type:          Simple
+Synopsis:            Library for creating and modifying zip archives.
+Description:         The zip-archive library provides functions for creating, modifying,
+                     and extracting files from zip archives.
+Category:            Codec
+License:             GPL
+License-file:        LICENSE
+Author:              John MacFarlane
+Maintainer:          jgm@berkeley.edu
+Build-Depends:       base
+Extra-Source-Files:  Tests.hs, COPYING, Data/Hash/CRC32/GZip.hs
+
+flag splitBase
+  Description:       Choose the new, smaller, split-up base package.
+  Default:           True
+flag executable
+  Description:       Build the Zip executable.
+  Default:           False
+
+Library 
+  if flag(splitBase)
+    Build-depends:   base >= 3, pretty, containers
+  else
+    Build-depends:   base < 3
+  Build-depends:     binary, zlib, filepath, directory, bytestring >= 0.9.0, array, mtl, utf8-string, old-time
+  Exposed-modules:   Codec.Archive.Zip
+  Hs-Source-Dirs:    .
+  Ghc-Options:       -Wall
+  Extensions:        CPP
+  if os(windows)
+    cpp-options:     -D_WINDOWS
+  else
+    Build-depends:   unix
+
+Executable Zip
+  if flag(executable)
+    Buildable:       True
+  else
+    Buildable:       False
+  Main-is:           Zip.hs
+  Ghc-Options:       -Wall
