module Codec.Archive.Zip.Util where import Control.Applicative hiding (many) import Data.Bits import Data.ByteString (ByteString) import qualified Data.ByteString as B import Data.Time import Data.Time.Clock.POSIX import Data.Word (Word16, Word32) import System.Time import Data.Conduit import qualified Data.Conduit.List as CL import Data.Digest.CRC32 import Data.Serialize.Get ifM :: Monad m => m Bool -> m a -> m a -> m a ifM cond conseq altern = do c <- cond if c then conseq else altern many :: (Monad m, Functor m) => m (Maybe a) -> m [a] many p = do r <- p case r of Just x -> (x:) <$> many p Nothing -> return [] ------------------------------------------------------------------------------ -- Serialize utils. maybeEmpty :: Get a -> Get (Maybe a) maybeEmpty p = do e <- isEmpty if e then return Nothing else Just <$> p runGet' :: Get a -> ByteString -> a runGet' g b = either error id $ runGet g b signature :: Word32 -> Get () signature sig = do s <- lookAhead getWord32le if s == sig then skip 4 else fail "Wrong signature." ------------------------------------------------------------------------------ -- Time utils. data MSDOSDateTime = MSDOSDateTime { msDOSDate :: Word16 , msDOSTime :: Word16 } deriving (Show) msDOSDateTimeToUTCTime :: MSDOSDateTime -> UTCTime msDOSDateTimeToUTCTime dosDateTime = UTCTime { utctDay = fromGregorian year month day , utctDayTime = secondsToDiffTime $ hours * 60 * 60 + minutes * 60 + seconds } where seconds = fromIntegral $ 2 * (dosTime .&. 0x1F) minutes = fromIntegral $ (shiftR dosTime 5) .&. 0x3F hours = fromIntegral $ shiftR dosTime 11 day = fromIntegral $ dosDate .&. 0x1F month = fromIntegral $ (shiftR dosDate 5) .&. 0xF year = 1980 + fromIntegral (shiftR dosDate 9) dosDate = msDOSDate dosDateTime dosTime = msDOSTime dosDateTime utcTimeToMSDOSDateTime :: UTCTime -> MSDOSDateTime utcTimeToMSDOSDateTime utcTime = MSDOSDateTime { msDOSDate = dosDate , msDOSTime = dosTime } where dosTime = fromIntegral $ seconds + shiftL minutes 5 + shiftL hours 11 dosDate = fromIntegral $ day + shiftL month 5 + shiftL year 9 seconds = fromEnum (todSec tod) `div` 2 minutes = todMin tod hours = todHour tod tod = timeToTimeOfDay $ utctDayTime utcTime year = fromIntegral year' - 1980 (year', month, day) = toGregorian $ utctDay utcTime clockTimeToUTCTime :: ClockTime -> UTCTime clockTimeToUTCTime (TOD seconds picoseconds) = let utcTime = posixSecondsToUTCTime $ fromIntegral seconds in utcTime { utctDayTime = utctDayTime utcTime + picosecondsToDiffTime picoseconds } ------------------------------------------------------------------------------ -- Conduit utils. crc32Sink :: Monad m => Sink ByteString m Word32 crc32Sink = CL.fold crc32Update 0 sizeSink :: Monad m => Sink ByteString m Int sizeSink = CL.fold (\acc input -> B.length input + acc) 0