zip-archive 0.1.2.1 → 0.1.3
raw patch · 3 files changed
+103/−117 lines, 3 filesdep ~binarydep ~directory
Dependency ranges changed: binary, directory
Files
- Codec/Archive/Zip.hs +101/−111
- tests/test-zip-archive.hs +0/−4
- zip-archive.cabal +2/−2
Codec/Archive/Zip.hs view
@@ -67,12 +67,12 @@ import Text.Printf import System.FilePath import System.Directory ( doesDirectoryExist, getDirectoryContents, createDirectoryIfMissing )-import Control.Monad ( when, unless, zipWithM, liftM )+import Control.Monad ( when, unless, zipWithM ) import System.Directory ( getModificationTime ) import System.IO ( stderr, hPutStrLn ) import qualified Data.Digest.CRC32 as CRC32 import qualified Data.Map as M-+import Control.Applicative #ifndef _WINDOWS import System.Posix.Files ( setFileTimes ) #endif@@ -263,15 +263,6 @@ -------------------------------------------------------------------------------- -- 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 =@@ -419,9 +410,10 @@ getArchive = do locals <- many getLocalFile files <- many (getFileHeader (M.fromList locals))- digSig <- lookAheadM getDigitalSignature+ digSig <- Just `fmap` getDigitalSignature <|> return Nothing endSig <- getWord32le- unless (endSig == 0x06054b50) $ fail "Did not find end of central directory signature"+ 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@@ -490,49 +482,47 @@ -- begins with the signature 0x08074b50, then 4 bytes crc-32, 4 bytes -- compressed size, 4 bytes uncompressed size. -getLocalFile :: Get (Maybe (Word32, B.ByteString))+getLocalFile :: Get (Word32, B.ByteString) getLocalFile = do- sig <- lookAhead getWord32le- if sig /= 0x04034b50- then return Nothing- else do- offset <- bytesRead- skip 4 -- signature- skip 2 -- version- bitflag <- getWord16le- 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 <- if bitflag .&. 0O10 == 0- then getLazyByteString (fromIntegral compressedSize)- else -- If bit 3 of general purpose bit flag is set,- -- then we need to read until we get to the- -- data descriptor record. We assume that the- -- record has signature 0x08074b50; this is not required- -- by the specification but is common.- do raw <- many $ do- s <- lookAhead getWord32le- if s == 0x08074b50- then return Nothing- else liftM Just getWord8- skip 4 -- signature- skip 4 -- crc32- cs <- getWord32le -- compressed size- skip 4 -- uncompressed size- if fromIntegral cs == length raw- then return $ B.pack raw- else fail "Content size mismatch in data descriptor record" - return $ Just (fromIntegral offset, compressedData)+ offset <- bytesRead+ getWord32le >>= ensure (== 0x04034b50)+ skip 2 -- version+ bitflag <- getWord16le+ 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 <- if bitflag .&. 0O10 == 0+ then getLazyByteString (fromIntegral compressedSize)+ else -- If bit 3 of general purpose bit flag is set,+ -- then we need to read until we get to the+ -- data descriptor record. We assume that the+ -- record has signature 0x08074b50; this is not required+ -- by the specification but is common.+ do raw <- getWordsTilSig 0x08074b50+ skip 4 -- crc32+ cs <- getWord32le -- compressed size+ skip 4 -- uncompressed size+ if fromIntegral cs == length raw+ then return $ B.pack raw+ else fail "Content size mismatch in data descriptor record" + return (fromIntegral offset, compressedData) +getWordsTilSig :: Word32 -> Get [Word8]+getWordsTilSig sig =+ (getWord32le >>= ensure (== sig) >> return []) <|>+ do w <- getWord8+ ws <- getWordsTilSig sig+ return (w:ws)+ putLocalFile :: Entry -> Put putLocalFile f = do putWord32le 0x04034b50@@ -579,57 +569,54 @@ -- > file comment (variable size) getFileHeader :: M.Map Word32 B.ByteString -- ^ map of (offset, content) pairs returned by getLocalFile- -> Get (Maybe Entry)+ -> Get 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 <- getWord8- skip 1 -- upper byte indicates OS part of "version needed to extract"- 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 (M.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- }+ getWord32le >>= ensure (== 0x02014b50)+ skip 2 -- version made by+ versionNeededToExtract <- getWord8+ skip 1 -- upper byte indicates OS part of "version needed to extract"+ 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 (M.lookup relativeOffset locals) of+ Just x -> return x+ Nothing -> fail $ "Unable to find data at offset " +++ show relativeOffset+ return $ 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@@ -666,14 +653,11 @@ -- > size of data 2 bytes -- > signature data (variable size) -getDigitalSignature :: Get (Maybe B.ByteString)+getDigitalSignature :: Get B.ByteString getDigitalSignature = do- hdrSig <- getWord32le- if hdrSig /= 0x08064b50- then return Nothing- else do- sigSize <- getWord16le- getLazyByteString (toEnum $ fromEnum sigSize) >>= return . Just+ getWord32le >>= ensure (== 0x05054b50)+ sigSize <- getWord16le+ getLazyByteString (toEnum $ fromEnum sigSize) putDigitalSignature :: Maybe B.ByteString -> Put putDigitalSignature Nothing = return ()@@ -681,3 +665,9 @@ putWord32le 0x08064b50 putWord16le $ fromIntegral $ B.length sig putLazyByteString sig++ensure :: (a -> Bool) -> a -> Get ()+ensure p val =+ if p val+ then return ()+ else fail "ensure not satisfied"
tests/test-zip-archive.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE CPP #-} -- Test suite for Codec.Archive.Zip -- runghc Test.hs @@ -6,9 +5,6 @@ import System.Directory import Test.HUnit.Base import Test.HUnit.Text-#if MIN_VERSION_directory(1,2,0)-import System.Time-#endif import System.Process import qualified Data.ByteString.Lazy as B import Control.Applicative
zip-archive.cabal view
@@ -1,5 +1,5 @@ Name: zip-archive-Version: 0.1.2.1+Version: 0.1.3 Cabal-Version: >= 1.10 Build-type: Simple Synopsis: Library for creating and modifying zip archives.@@ -29,7 +29,7 @@ Build-depends: base >= 3 && < 5, pretty, containers else Build-depends: base < 3- Build-depends: binary < 0.6, zlib, filepath, bytestring >= 0.9.0, array, mtl, utf8-string >= 0.3.1, old-time, digest >= 0.0.0.1, directory, time+ Build-depends: binary >= 0.6, zlib, filepath, bytestring >= 0.9.0, array, mtl, utf8-string >= 0.3.1, old-time, digest >= 0.0.0.1, directory, time Exposed-modules: Codec.Archive.Zip Default-Language: Haskell98 Hs-Source-Dirs: .