{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE RankNTypes #-}
module Codec.Archive.Zip.Conduit.UnZip
( unZipStream
, ZipEntry(..)
, ZipInfo(..)
) where
import Control.Applicative ((<|>), empty)
import Control.Monad (when, unless, guard)
import Control.Monad.Catch (MonadThrow)
import Control.Monad.Primitive (PrimMonad)
import qualified Data.Binary.Get as G
import Data.Bits ((.&.), testBit, clearBit, shiftL, shiftR)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BSC
import qualified Data.Conduit as C
import qualified Data.Conduit.Combinators as CC
import Data.Conduit.Serialization.Binary (sinkGet)
import qualified Data.Conduit.Zlib as CZ
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
import Data.Time (LocalTime(..), TimeOfDay(..), fromGregorian)
import Data.Word (Word16, Word32, Word64)
import Codec.Archive.Zip.Conduit.Types
import Codec.Archive.Zip.Conduit.Internal
data m
=
{ forall (m :: * -> *).
Header m -> ConduitM ByteString ByteString m ()
fileDecompress :: C.ConduitM BS.ByteString BS.ByteString m ()
, forall (m :: * -> *). Header m -> ZipEntry
fileEntry :: !ZipEntry
, forall (m :: * -> *). Header m -> Word32
fileCRC :: !Word32
, forall (m :: * -> *). Header m -> Word64
fileCSize :: !Word64
, forall (m :: * -> *). Header m -> Bool
fileZip64 :: !Bool
}
| EndOfCentralDirectory
{ forall (m :: * -> *). Header m -> ZipInfo
endInfo :: ZipInfo
}
data ExtField = ExtField
{ ExtField -> Bool
extZip64 :: Bool
, ExtField -> Word64
extZip64USize
, ExtField -> Word64
extZip64CSize :: Word64
}
pass :: (MonadThrow m, Integral n) => n -> C.ConduitM BS.ByteString BS.ByteString m ()
pass :: forall (m :: * -> *) n.
(MonadThrow m, Integral n) =>
n -> ConduitM ByteString ByteString m ()
pass n
0 = forall (m :: * -> *) a. Monad m => a -> m a
return ()
pass n
n = forall (m :: * -> *) i o. Monad m => ConduitT i o m (Maybe i)
C.await forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= forall b a. b -> (a -> b) -> Maybe a -> b
maybe
(forall (m :: * -> *) a. MonadThrow m => String -> m a
zipError forall a b. (a -> b) -> a -> b
$ String
"EOF in file data, expecting " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show Year
ni forall a. [a] -> [a] -> [a]
++ String
" more bytes")
(\ByteString
b ->
let n' :: Year
n' = Year
ni forall a. Num a => a -> a -> a
- forall a. Integral a => a -> Year
toInteger (ByteString -> MonthOfYear
BS.length ByteString
b) in
if Year
n' forall a. Ord a => a -> a -> Bool
< Year
0
then do
let (ByteString
b', ByteString
r) = MonthOfYear -> ByteString -> (ByteString, ByteString)
BS.splitAt (forall a b. (Integral a, Num b) => a -> b
fromIntegral n
n) ByteString
b
forall (m :: * -> *) o i. Monad m => o -> ConduitT i o m ()
C.yield ByteString
b'
forall i o (m :: * -> *). i -> ConduitT i o m ()
C.leftover ByteString
r
else do
forall (m :: * -> *) o i. Monad m => o -> ConduitT i o m ()
C.yield ByteString
b
forall (m :: * -> *) n.
(MonadThrow m, Integral n) =>
n -> ConduitM ByteString ByteString m ()
pass Year
n')
where ni :: Year
ni = forall a. Integral a => a -> Year
toInteger n
n
foldGet :: (a -> G.Get a) -> a -> G.Get a
foldGet :: forall a. (a -> Get a) -> a -> Get a
foldGet a -> Get a
g a
z = do
Bool
e <- Get Bool
G.isEmpty
if Bool
e then forall (m :: * -> *) a. Monad m => a -> m a
return a
z else a -> Get a
g a
z forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= forall a. (a -> Get a) -> a -> Get a
foldGet a -> Get a
g
fromDOSTime :: Word16 -> Word16 -> LocalTime
fromDOSTime :: Word16 -> Word16 -> LocalTime
fromDOSTime Word16
time Word16
date = Day -> TimeOfDay -> LocalTime
LocalTime
(Year -> MonthOfYear -> MonthOfYear -> Day
fromGregorian
(forall a b. (Integral a, Num b) => a -> b
fromIntegral forall a b. (a -> b) -> a -> b
$ Word16
date forall a. Bits a => a -> MonthOfYear -> a
`shiftR` MonthOfYear
9 forall a. Num a => a -> a -> a
+ Word16
1980)
(forall a b. (Integral a, Num b) => a -> b
fromIntegral forall a b. (a -> b) -> a -> b
$ Word16
date forall a. Bits a => a -> MonthOfYear -> a
`shiftR` MonthOfYear
5 forall a. Bits a => a -> a -> a
.&. Word16
0x0f)
(forall a b. (Integral a, Num b) => a -> b
fromIntegral forall a b. (a -> b) -> a -> b
$ Word16
date forall a. Bits a => a -> a -> a
.&. Word16
0x1f))
(MonthOfYear -> MonthOfYear -> Pico -> TimeOfDay
TimeOfDay
(forall a b. (Integral a, Num b) => a -> b
fromIntegral forall a b. (a -> b) -> a -> b
$ Word16
time forall a. Bits a => a -> MonthOfYear -> a
`shiftR` MonthOfYear
11)
(forall a b. (Integral a, Num b) => a -> b
fromIntegral forall a b. (a -> b) -> a -> b
$ Word16
time forall a. Bits a => a -> MonthOfYear -> a
`shiftR` MonthOfYear
5 forall a. Bits a => a -> a -> a
.&. Word16
0x3f)
(forall a b. (Integral a, Num b) => a -> b
fromIntegral forall a b. (a -> b) -> a -> b
$ Word16
time forall a. Bits a => a -> MonthOfYear -> a
`shiftL` MonthOfYear
1 forall a. Bits a => a -> a -> a
.&. Word16
0x3f))
unZipStream ::
( MonadThrow m
, PrimMonad m
) => C.ConduitM BS.ByteString (Either ZipEntry BS.ByteString) m ZipInfo
unZipStream :: forall (m :: * -> *).
(MonadThrow m, PrimMonad m) =>
ConduitM ByteString (Either ZipEntry ByteString) m ZipInfo
unZipStream = ConduitT ByteString (Either ZipEntry ByteString) m ZipInfo
next where
next :: ConduitT ByteString (Either ZipEntry ByteString) m ZipInfo
next = do
Header m
h <- forall (m :: * -> *) b z.
MonadThrow m =>
Get b -> ConduitT ByteString z m b
sinkGet forall a b. (a -> b) -> a -> b
$ do
Word32
sig <- Get Word32
G.getWord32le
case Word32
sig of
Word32
0x04034b50 -> Get (Header m)
fileHeader
Word32
_ -> forall {m :: * -> *}. Word32 -> Get (Header m)
centralBody Word32
sig
case Header m
h of
FileHeader{Bool
Word32
Word64
ConduitM ByteString ByteString m ()
ZipEntry
fileZip64 :: Bool
fileCSize :: Word64
fileCRC :: Word32
fileEntry :: ZipEntry
fileDecompress :: ConduitM ByteString ByteString m ()
fileZip64 :: forall (m :: * -> *). Header m -> Bool
fileCSize :: forall (m :: * -> *). Header m -> Word64
fileCRC :: forall (m :: * -> *). Header m -> Word32
fileEntry :: forall (m :: * -> *). Header m -> ZipEntry
fileDecompress :: forall (m :: * -> *).
Header m -> ConduitM ByteString ByteString m ()
..} -> do
forall (m :: * -> *) o i. Monad m => o -> ConduitT i o m ()
C.yield forall a b. (a -> b) -> a -> b
$ forall a b. a -> Either a b
Left ZipEntry
fileEntry
Bool
r <- forall (m :: * -> *) o1 o2 i r.
Monad m =>
(o1 -> o2) -> ConduitT i o1 m r -> ConduitT i o2 m r
C.mapOutput forall a b. b -> Either a b
Right forall a b. (a -> b) -> a -> b
$
case ZipEntry -> Maybe Word64
zipEntrySize ZipEntry
fileEntry of
Maybe Word64
Nothing -> do
(Word64
csize, (Word64
size, Word32
crc)) <- forall (m :: * -> *) o.
Monad m =>
ConduitT ByteString o m () -> ConduitT ByteString o m Word64
inputSize ConduitM ByteString ByteString m ()
fileDecompress forall (m :: * -> *) a b r1 c r2.
Monad m =>
ConduitT a b m r1 -> ConduitT b c m r2 -> ConduitT a c m (r1, r2)
`C.fuseBoth` forall (m :: * -> *).
Monad m =>
ConduitT ByteString ByteString m (Word64, Word32)
sizeCRC
forall (m :: * -> *) b z.
MonadThrow m =>
Get b -> ConduitT ByteString z m b
sinkGet forall a b. (a -> b) -> a -> b
$ forall {m :: * -> *}. Header m -> Get Bool
dataDesc Header m
h
{ fileCSize :: Word64
fileCSize = Word64
csize
, fileCRC :: Word32
fileCRC = Word32
crc
, fileEntry :: ZipEntry
fileEntry = ZipEntry
fileEntry
{ zipEntrySize :: Maybe Word64
zipEntrySize = forall a. a -> Maybe a
Just Word64
size
}
}
Just Word64
usize -> do
(Word64
size, Word32
crc) <- forall (m :: * -> *) n.
(MonadThrow m, Integral n) =>
n -> ConduitM ByteString ByteString m ()
pass Word64
fileCSize
forall (m :: * -> *) a b c r.
Monad m =>
ConduitT a b m () -> ConduitT b c m r -> ConduitT a c m r
C..| (ConduitM ByteString ByteString m ()
fileDecompress forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> forall (m :: * -> *) a o. Monad m => ConduitT a o m ()
CC.sinkNull)
forall (m :: * -> *) a b c r.
Monad m =>
ConduitT a b m () -> ConduitT b c m r -> ConduitT a c m r
C..| forall (m :: * -> *).
Monad m =>
ConduitT ByteString ByteString m (Word64, Word32)
sizeCRC
forall (m :: * -> *) b z.
MonadThrow m =>
Get b -> ConduitT ByteString z m b
sinkGet forall a b. (a -> b) -> a -> b
$ (forall (f :: * -> *). Alternative f => Bool -> f ()
guard forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< forall {m :: * -> *}. Header m -> Get Bool
dataDesc Header m
h) forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> forall (m :: * -> *) a. Monad m => a -> m a
return ()
forall (m :: * -> *) a. Monad m => a -> m a
return (Word64
size forall a. Eq a => a -> a -> Bool
== Word64
usize Bool -> Bool -> Bool
&& Word32
crc forall a. Eq a => a -> a -> Bool
== Word32
fileCRC)
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless Bool
r forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. MonadThrow m => String -> m a
zipError forall a b. (a -> b) -> a -> b
$ forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either Text -> String
T.unpack ByteString -> String
BSC.unpack (ZipEntry -> Either Text ByteString
zipEntryName ZipEntry
fileEntry) forall a. [a] -> [a] -> [a]
++ String
": data integrity check failed"
ConduitT ByteString (Either ZipEntry ByteString) m ZipInfo
next
EndOfCentralDirectory{ZipInfo
endInfo :: ZipInfo
endInfo :: forall (m :: * -> *). Header m -> ZipInfo
..} -> do
forall (m :: * -> *) a. Monad m => a -> m a
return ZipInfo
endInfo
dataDesc :: Header m -> Get Bool
dataDesc Header m
h =
(do
Word32
sig <- Get Word32
G.getWord32le
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Word32
sig forall a. Eq a => a -> a -> Bool
== Word32
0x08074b50)
forall {m :: * -> *}. Header m -> Get Bool
dataDescBody Header m
h)
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> forall {m :: * -> *}. Header m -> Get Bool
dataDescBody Header m
h
dataDescBody :: Header m -> Get Bool
dataDescBody FileHeader{Bool
Word32
Word64
ConduitM ByteString ByteString m ()
ZipEntry
fileZip64 :: Bool
fileCSize :: Word64
fileCRC :: Word32
fileEntry :: ZipEntry
fileDecompress :: ConduitM ByteString ByteString m ()
fileZip64 :: forall (m :: * -> *). Header m -> Bool
fileCSize :: forall (m :: * -> *). Header m -> Word64
fileCRC :: forall (m :: * -> *). Header m -> Word32
fileEntry :: forall (m :: * -> *). Header m -> ZipEntry
fileDecompress :: forall (m :: * -> *).
Header m -> ConduitM ByteString ByteString m ()
..} = do
Word32
crc <- Get Word32
G.getWord32le
let getSize :: Get Word64
getSize = if Bool
fileZip64 then Get Word64
G.getWord64le else forall a b. (Integral a, Num b) => a -> b
fromIntegral forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Word32
G.getWord32le
Word64
csiz <- Get Word64
getSize
Word64
usiz <- Get Word64
getSize
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ Word32
crc forall a. Eq a => a -> a -> Bool
== Word32
fileCRC Bool -> Bool -> Bool
&& Word64
csiz forall a. Eq a => a -> a -> Bool
== Word64
fileCSize Bool -> Bool -> Bool
&& (Word64
usiz forall a. Eq a => a -> a -> Bool
==) forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
`all` ZipEntry -> Maybe Word64
zipEntrySize ZipEntry
fileEntry
dataDescBody Header m
_ = forall (f :: * -> *) a. Alternative f => f a
empty
central :: Get (Header m)
central = Get Word32
G.getWord32le forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Word32 -> Get (Header m)
centralBody
centralBody :: Word32 -> Get (Header m)
centralBody Word32
0x02014b50 = Get ()
centralHeader forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Get (Header m)
central
centralBody Word32
0x06064b50 = Get ()
zip64EndDirectory forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Get (Header m)
central
centralBody Word32
0x07064b50 = MonthOfYear -> Get ()
G.skip MonthOfYear
16 forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Get (Header m)
central
centralBody Word32
0x06054b50 = forall (m :: * -> *). ZipInfo -> Header m
EndOfCentralDirectory forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get ZipInfo
endDirectory
centralBody Word32
sig = forall (m :: * -> *) a. MonadFail m => String -> m a
fail forall a b. (a -> b) -> a -> b
$ String
"Unknown header signature: " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show Word32
sig
fileHeader :: Get (Header m)
fileHeader = do
Word8
ver <- Get Word8
G.getWord8
Word8
_os <- Get Word8
G.getWord8
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Word8
ver forall a. Ord a => a -> a -> Bool
> Word8
zipVersion) forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. MonadFail m => String -> m a
fail forall a b. (a -> b) -> a -> b
$ String
"Unsupported version: " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show Word8
ver
Word16
gpf <- Get Word16
G.getWord16le
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Word16
gpf forall a. Bits a => a -> MonthOfYear -> a
`clearBit` MonthOfYear
1 forall a. Bits a => a -> MonthOfYear -> a
`clearBit` MonthOfYear
2 forall a. Bits a => a -> MonthOfYear -> a
`clearBit` MonthOfYear
3 forall a. Bits a => a -> MonthOfYear -> a
`clearBit` MonthOfYear
11 forall a. Eq a => a -> a -> Bool
/= Word16
0) forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. MonadFail m => String -> m a
fail forall a b. (a -> b) -> a -> b
$ String
"Unsupported flags: " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show Word16
gpf
Word16
comp <- Get Word16
G.getWord16le
ConduitM ByteString ByteString m ()
dcomp <- case Word16
comp of
Word16
0 | forall a. Bits a => a -> MonthOfYear -> Bool
testBit Word16
gpf MonthOfYear
3 -> forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"Unsupported uncompressed streaming file data"
| Bool
otherwise -> forall (m :: * -> *) a. Monad m => a -> m a
return forall (m :: * -> *) a. Monad m => ConduitT a a m ()
idConduit
Word16
8 -> forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *).
(PrimMonad m, MonadThrow m) =>
WindowBits -> ConduitT ByteString ByteString m ()
CZ.decompress WindowBits
deflateWindowBits
Word16
_ -> forall (m :: * -> *) a. MonadFail m => String -> m a
fail forall a b. (a -> b) -> a -> b
$ String
"Unsupported compression method: " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show Word16
comp
LocalTime
time <- Word16 -> Word16 -> LocalTime
fromDOSTime forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Word16
G.getWord16le forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Get Word16
G.getWord16le
Word32
crc <- Get Word32
G.getWord32le
Word32
csiz <- Get Word32
G.getWord32le
Word32
usiz <- Get Word32
G.getWord32le
MonthOfYear
nlen <- forall a b. (Integral a, Num b) => a -> b
fromIntegral forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Word16
G.getWord16le
MonthOfYear
elen <- forall a b. (Integral a, Num b) => a -> b
fromIntegral forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Word16
G.getWord16le
ByteString
name <- MonthOfYear -> Get ByteString
G.getByteString MonthOfYear
nlen
let getExt :: ExtField -> Get ExtField
getExt ExtField
ext = do
Word16
t <- Get Word16
G.getWord16le
MonthOfYear
z <- forall a b. (Integral a, Num b) => a -> b
fromIntegral forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Word16
G.getWord16le
forall a. MonthOfYear -> Get a -> Get a
G.isolate MonthOfYear
z forall a b. (a -> b) -> a -> b
$ case Word16
t of
Word16
0x0001 -> do
Word64
usiz' <- if Word32
usiz forall a. Eq a => a -> a -> Bool
== forall n. Integral n => n
maxBound32 then Get Word64
G.getWord64le else forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ ExtField -> Word64
extZip64USize ExtField
ext
Word64
csiz' <- if Word32
csiz forall a. Eq a => a -> a -> Bool
== forall n. Integral n => n
maxBound32 then Get Word64
G.getWord64le else forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ ExtField -> Word64
extZip64CSize ExtField
ext
forall (m :: * -> *) a. Monad m => a -> m a
return ExtField
ext
{ extZip64 :: Bool
extZip64 = Bool
True
, extZip64USize :: Word64
extZip64USize = Word64
usiz'
, extZip64CSize :: Word64
extZip64CSize = Word64
csiz'
}
Word16
_ -> ExtField
ext forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ MonthOfYear -> Get ()
G.skip MonthOfYear
z
ExtField{Bool
Word64
extZip64CSize :: Word64
extZip64USize :: Word64
extZip64 :: Bool
extZip64CSize :: ExtField -> Word64
extZip64USize :: ExtField -> Word64
extZip64 :: ExtField -> Bool
..} <- forall a. MonthOfYear -> Get a -> Get a
G.isolate MonthOfYear
elen forall a b. (a -> b) -> a -> b
$ forall a. (a -> Get a) -> a -> Get a
foldGet ExtField -> Get ExtField
getExt ExtField
{ extZip64 :: Bool
extZip64 = Bool
False
, extZip64USize :: Word64
extZip64USize = forall a b. (Integral a, Num b) => a -> b
fromIntegral Word32
usiz
, extZip64CSize :: Word64
extZip64CSize = forall a b. (Integral a, Num b) => a -> b
fromIntegral Word32
csiz
}
forall (m :: * -> *) a. Monad m => a -> m a
return FileHeader
{ fileEntry :: ZipEntry
fileEntry = ZipEntry
{ zipEntryName :: Either Text ByteString
zipEntryName = if forall a. Bits a => a -> MonthOfYear -> Bool
testBit Word16
gpf MonthOfYear
11 then forall a b. a -> Either a b
Left (ByteString -> Text
TE.decodeUtf8 ByteString
name) else forall a b. b -> Either a b
Right ByteString
name
, zipEntryTime :: LocalTime
zipEntryTime = LocalTime
time
, zipEntrySize :: Maybe Word64
zipEntrySize = if forall a. Bits a => a -> MonthOfYear -> Bool
testBit Word16
gpf MonthOfYear
3 then forall a. Maybe a
Nothing else forall a. a -> Maybe a
Just Word64
extZip64USize
, zipEntryExternalAttributes :: Maybe Word32
zipEntryExternalAttributes = forall a. Maybe a
Nothing
}
, fileDecompress :: ConduitM ByteString ByteString m ()
fileDecompress = ConduitM ByteString ByteString m ()
dcomp
, fileCSize :: Word64
fileCSize = Word64
extZip64CSize
, fileCRC :: Word32
fileCRC = Word32
crc
, fileZip64 :: Bool
fileZip64 = Bool
extZip64
}
centralHeader :: Get ()
centralHeader = do
MonthOfYear -> Get ()
G.skip MonthOfYear
24
MonthOfYear
nlen <- forall a b. (Integral a, Num b) => a -> b
fromIntegral forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Word16
G.getWord16le
MonthOfYear
elen <- forall a b. (Integral a, Num b) => a -> b
fromIntegral forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Word16
G.getWord16le
MonthOfYear
clen <- forall a b. (Integral a, Num b) => a -> b
fromIntegral forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Word16
G.getWord16le
MonthOfYear -> Get ()
G.skip forall a b. (a -> b) -> a -> b
$ MonthOfYear
12 forall a. Num a => a -> a -> a
+ MonthOfYear
nlen forall a. Num a => a -> a -> a
+ MonthOfYear
elen forall a. Num a => a -> a -> a
+ MonthOfYear
clen
zip64EndDirectory :: Get ()
zip64EndDirectory = do
Word64
len <- Get Word64
G.getWord64le
MonthOfYear -> Get ()
G.skip forall a b. (a -> b) -> a -> b
$ forall a b. (Integral a, Num b) => a -> b
fromIntegral Word64
len
endDirectory :: Get ZipInfo
endDirectory = do
MonthOfYear -> Get ()
G.skip MonthOfYear
16
MonthOfYear
clen <- forall a b. (Integral a, Num b) => a -> b
fromIntegral forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Get Word16
G.getWord16le
ByteString
comm <- MonthOfYear -> Get ByteString
G.getByteString MonthOfYear
clen
forall (m :: * -> *) a. Monad m => a -> m a
return ZipInfo
{ zipComment :: ByteString
zipComment = ByteString
comm
}