{-# LANGUAGE CPP #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeFamilies #-}
module Codec.Archive.Zip
(
EntrySelector,
mkEntrySelector,
unEntrySelector,
getEntryName,
EntrySelectorException (..),
EntryDescription (..),
CompressionMethod (..),
ArchiveDescription (..),
ZipException (..),
ZipArchive,
ZipState,
createArchive,
withArchive,
getEntries,
doesEntryExist,
getEntryDesc,
getEntry,
getEntrySource,
sourceEntry,
saveEntry,
checkEntry,
unpackInto,
getArchiveComment,
getArchiveDescription,
addEntry,
sinkEntry,
loadEntry,
copyEntry,
packDirRecur,
packDirRecur',
renameEntry,
deleteEntry,
recompress,
setEntryComment,
deleteEntryComment,
setModTime,
addExtraField,
deleteExtraField,
setExternalFileAttrs,
forEntries,
setArchiveComment,
deleteArchiveComment,
undoEntryChanges,
undoArchiveChanges,
undoAll,
commit,
)
where
import qualified Codec.Archive.Zip.Internal as I
import Codec.Archive.Zip.Type
import Conduit (PrimMonad)
import Control.Monad
import Control.Monad.Base (MonadBase (..))
import Control.Monad.Catch
import Control.Monad.State.Strict
import Control.Monad.Trans.Control (MonadBaseControl (..))
import Control.Monad.Trans.Resource (MonadResource, ResourceT)
import Data.ByteString (ByteString)
import Data.Conduit (ConduitT, (.|))
import qualified Data.Conduit as C
import qualified Data.Conduit.Binary as CB
import qualified Data.Conduit.List as CL
import qualified Data.DList as DList
import Data.Map.Strict (Map, (!))
import qualified Data.Map.Strict as M
import Data.Sequence (Seq, (|>))
import qualified Data.Sequence as S
import qualified Data.Set as E
import Data.Text (Text)
import Data.Time.Clock (UTCTime)
import Data.Void
import Data.Word (Word16, Word32)
import System.Directory
import System.FilePath ((</>))
import qualified System.FilePath as FP
import System.IO.Error (isDoesNotExistError)
#ifndef mingw32_HOST_OS
import qualified Codec.Archive.Zip.Unix as Unix
import qualified System.Posix as Unix
#endif
newtype ZipArchive a = ZipArchive
{ forall a. ZipArchive a -> StateT ZipState IO a
unZipArchive :: StateT ZipState IO a
}
deriving
( forall a b. a -> ZipArchive b -> ZipArchive a
forall a b. (a -> b) -> ZipArchive a -> ZipArchive b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: forall a b. a -> ZipArchive b -> ZipArchive a
$c<$ :: forall a b. a -> ZipArchive b -> ZipArchive a
fmap :: forall a b. (a -> b) -> ZipArchive a -> ZipArchive b
$cfmap :: forall a b. (a -> b) -> ZipArchive a -> ZipArchive b
Functor,
Functor ZipArchive
forall a. a -> ZipArchive a
forall a b. ZipArchive a -> ZipArchive b -> ZipArchive a
forall a b. ZipArchive a -> ZipArchive b -> ZipArchive b
forall a b. ZipArchive (a -> b) -> ZipArchive a -> ZipArchive b
forall a b c.
(a -> b -> c) -> ZipArchive a -> ZipArchive b -> ZipArchive c
forall (f :: * -> *).
Functor f
-> (forall a. a -> f a)
-> (forall a b. f (a -> b) -> f a -> f b)
-> (forall a b c. (a -> b -> c) -> f a -> f b -> f c)
-> (forall a b. f a -> f b -> f b)
-> (forall a b. f a -> f b -> f a)
-> Applicative f
<* :: forall a b. ZipArchive a -> ZipArchive b -> ZipArchive a
$c<* :: forall a b. ZipArchive a -> ZipArchive b -> ZipArchive a
*> :: forall a b. ZipArchive a -> ZipArchive b -> ZipArchive b
$c*> :: forall a b. ZipArchive a -> ZipArchive b -> ZipArchive b
liftA2 :: forall a b c.
(a -> b -> c) -> ZipArchive a -> ZipArchive b -> ZipArchive c
$cliftA2 :: forall a b c.
(a -> b -> c) -> ZipArchive a -> ZipArchive b -> ZipArchive c
<*> :: forall a b. ZipArchive (a -> b) -> ZipArchive a -> ZipArchive b
$c<*> :: forall a b. ZipArchive (a -> b) -> ZipArchive a -> ZipArchive b
pure :: forall a. a -> ZipArchive a
$cpure :: forall a. a -> ZipArchive a
Applicative,
Applicative ZipArchive
forall a. a -> ZipArchive a
forall a b. ZipArchive a -> ZipArchive b -> ZipArchive b
forall a b. ZipArchive a -> (a -> ZipArchive b) -> ZipArchive b
forall (m :: * -> *).
Applicative m
-> (forall a b. m a -> (a -> m b) -> m b)
-> (forall a b. m a -> m b -> m b)
-> (forall a. a -> m a)
-> Monad m
return :: forall a. a -> ZipArchive a
$creturn :: forall a. a -> ZipArchive a
>> :: forall a b. ZipArchive a -> ZipArchive b -> ZipArchive b
$c>> :: forall a b. ZipArchive a -> ZipArchive b -> ZipArchive b
>>= :: forall a b. ZipArchive a -> (a -> ZipArchive b) -> ZipArchive b
$c>>= :: forall a b. ZipArchive a -> (a -> ZipArchive b) -> ZipArchive b
Monad,
Monad ZipArchive
forall a. IO a -> ZipArchive a
forall (m :: * -> *).
Monad m -> (forall a. IO a -> m a) -> MonadIO m
liftIO :: forall a. IO a -> ZipArchive a
$cliftIO :: forall a. IO a -> ZipArchive a
MonadIO,
Monad ZipArchive
forall e a. Exception e => e -> ZipArchive a
forall (m :: * -> *).
Monad m -> (forall e a. Exception e => e -> m a) -> MonadThrow m
throwM :: forall e a. Exception e => e -> ZipArchive a
$cthrowM :: forall e a. Exception e => e -> ZipArchive a
MonadThrow,
MonadThrow ZipArchive
forall e a.
Exception e =>
ZipArchive a -> (e -> ZipArchive a) -> ZipArchive a
forall (m :: * -> *).
MonadThrow m
-> (forall e a. Exception e => m a -> (e -> m a) -> m a)
-> MonadCatch m
catch :: forall e a.
Exception e =>
ZipArchive a -> (e -> ZipArchive a) -> ZipArchive a
$ccatch :: forall e a.
Exception e =>
ZipArchive a -> (e -> ZipArchive a) -> ZipArchive a
MonadCatch,
MonadCatch ZipArchive
forall b.
((forall a. ZipArchive a -> ZipArchive a) -> ZipArchive b)
-> ZipArchive b
forall a b c.
ZipArchive a
-> (a -> ExitCase b -> ZipArchive c)
-> (a -> ZipArchive b)
-> ZipArchive (b, c)
forall (m :: * -> *).
MonadCatch m
-> (forall b. ((forall a. m a -> m a) -> m b) -> m b)
-> (forall b. ((forall a. m a -> m a) -> m b) -> m b)
-> (forall a b c.
m a -> (a -> ExitCase b -> m c) -> (a -> m b) -> m (b, c))
-> MonadMask m
generalBracket :: forall a b c.
ZipArchive a
-> (a -> ExitCase b -> ZipArchive c)
-> (a -> ZipArchive b)
-> ZipArchive (b, c)
$cgeneralBracket :: forall a b c.
ZipArchive a
-> (a -> ExitCase b -> ZipArchive c)
-> (a -> ZipArchive b)
-> ZipArchive (b, c)
uninterruptibleMask :: forall b.
((forall a. ZipArchive a -> ZipArchive a) -> ZipArchive b)
-> ZipArchive b
$cuninterruptibleMask :: forall b.
((forall a. ZipArchive a -> ZipArchive a) -> ZipArchive b)
-> ZipArchive b
mask :: forall b.
((forall a. ZipArchive a -> ZipArchive a) -> ZipArchive b)
-> ZipArchive b
$cmask :: forall b.
((forall a. ZipArchive a -> ZipArchive a) -> ZipArchive b)
-> ZipArchive b
MonadMask
)
instance MonadBase IO ZipArchive where
liftBase :: forall a. IO a -> ZipArchive a
liftBase = forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO
instance MonadBaseControl IO ZipArchive where
type StM ZipArchive a = (a, ZipState)
liftBaseWith :: forall a. (RunInBase ZipArchive IO -> IO a) -> ZipArchive a
liftBaseWith RunInBase ZipArchive IO -> IO a
f = forall a. StateT ZipState IO a -> ZipArchive a
ZipArchive forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall s (m :: * -> *) a. (s -> m (a, s)) -> StateT s m a
StateT forall a b. (a -> b) -> a -> b
$ \ZipState
s ->
(,ZipState
s) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> RunInBase ZipArchive IO -> IO a
f (forall a b c. (a -> b -> c) -> b -> a -> c
flip forall s (m :: * -> *) a. StateT s m a -> s -> m (a, s)
runStateT ZipState
s forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. ZipArchive a -> StateT ZipState IO a
unZipArchive)
{-# INLINEABLE liftBaseWith #-}
restoreM :: forall a. StM ZipArchive a -> ZipArchive a
restoreM = forall a. StateT ZipState IO a -> ZipArchive a
ZipArchive forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall s (m :: * -> *) a. (s -> m (a, s)) -> StateT s m a
StateT forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. a -> b -> a
const forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *) a. Monad m => a -> m a
return
{-# INLINEABLE restoreM #-}
data ZipState = ZipState
{
ZipState -> FilePath
zsFilePath :: FilePath,
ZipState -> Map EntrySelector EntryDescription
zsEntries :: Map EntrySelector EntryDescription,
ZipState -> ArchiveDescription
zsArchive :: ArchiveDescription,
ZipState -> Seq PendingAction
zsActions :: Seq I.PendingAction
}
createArchive ::
(MonadIO m) =>
FilePath ->
ZipArchive a ->
m a
createArchive :: forall (m :: * -> *) a.
MonadIO m =>
FilePath -> ZipArchive a -> m a
createArchive FilePath
path ZipArchive a
m = forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ do
FilePath
apath <- FilePath -> IO FilePath
makeAbsolute FilePath
path
IO () -> IO ()
ignoringAbsence (FilePath -> IO ()
removeFile FilePath
apath)
let st :: ZipState
st =
ZipState
{ zsFilePath :: FilePath
zsFilePath = FilePath
apath,
zsEntries :: Map EntrySelector EntryDescription
zsEntries = forall k a. Map k a
M.empty,
zsArchive :: ArchiveDescription
zsArchive = Maybe Text -> Natural -> Natural -> ArchiveDescription
ArchiveDescription forall a. Maybe a
Nothing Natural
0 Natural
0,
zsActions :: Seq PendingAction
zsActions = forall a. Seq a
S.empty
}
action :: StateT ZipState IO a
action = forall a. ZipArchive a -> StateT ZipState IO a
unZipArchive (ZipArchive a
m forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* ZipArchive ()
commit)
forall (m :: * -> *) s a. Monad m => StateT s m a -> s -> m a
evalStateT StateT ZipState IO a
action ZipState
st
withArchive ::
(MonadIO m) =>
FilePath ->
ZipArchive a ->
m a
withArchive :: forall (m :: * -> *) a.
MonadIO m =>
FilePath -> ZipArchive a -> m a
withArchive FilePath
path ZipArchive a
m = forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ do
FilePath
apath <- FilePath -> IO FilePath
canonicalizePath FilePath
path
(ArchiveDescription
desc, Map EntrySelector EntryDescription
entries) <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (FilePath
-> IO (ArchiveDescription, Map EntrySelector EntryDescription)
I.scanArchive FilePath
apath)
let st :: ZipState
st =
ZipState
{ zsFilePath :: FilePath
zsFilePath = FilePath
apath,
zsEntries :: Map EntrySelector EntryDescription
zsEntries = Map EntrySelector EntryDescription
entries,
zsArchive :: ArchiveDescription
zsArchive = ArchiveDescription
desc,
zsActions :: Seq PendingAction
zsActions = forall a. Seq a
S.empty
}
action :: StateT ZipState IO a
action = forall a. ZipArchive a -> StateT ZipState IO a
unZipArchive (ZipArchive a
m forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* ZipArchive ()
commit)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (forall (m :: * -> *) s a. Monad m => StateT s m a -> s -> m a
evalStateT StateT ZipState IO a
action ZipState
st)
getEntries :: ZipArchive (Map EntrySelector EntryDescription)
getEntries :: ZipArchive (Map EntrySelector EntryDescription)
getEntries = forall a. StateT ZipState IO a -> ZipArchive a
ZipArchive (forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets ZipState -> Map EntrySelector EntryDescription
zsEntries)
doesEntryExist :: EntrySelector -> ZipArchive Bool
doesEntryExist :: EntrySelector -> ZipArchive Bool
doesEntryExist EntrySelector
s = forall k a. Ord k => k -> Map k a -> Bool
M.member EntrySelector
s forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ZipArchive (Map EntrySelector EntryDescription)
getEntries
getEntryDesc :: EntrySelector -> ZipArchive (Maybe EntryDescription)
getEntryDesc :: EntrySelector -> ZipArchive (Maybe EntryDescription)
getEntryDesc EntrySelector
s = forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup EntrySelector
s forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ZipArchive (Map EntrySelector EntryDescription)
getEntries
getEntry ::
EntrySelector ->
ZipArchive ByteString
getEntry :: EntrySelector -> ZipArchive ByteString
getEntry EntrySelector
s = forall a.
EntrySelector
-> ConduitT ByteString Void (ResourceT IO) a -> ZipArchive a
sourceEntry EntrySelector
s (forall (m :: * -> *) b a o.
(Monad m, Monoid b) =>
(a -> b) -> ConduitT a o m b
CL.foldMap forall a. a -> a
id)
getEntrySource ::
(PrimMonad m, MonadThrow m, MonadResource m) =>
EntrySelector ->
ZipArchive (ConduitT () ByteString m ())
getEntrySource :: forall (m :: * -> *).
(PrimMonad m, MonadThrow m, MonadResource m) =>
EntrySelector -> ZipArchive (ConduitT () ByteString m ())
getEntrySource EntrySelector
s = do
FilePath
path <- ZipArchive FilePath
getFilePath
Maybe EntryDescription
mdesc <- forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup EntrySelector
s forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ZipArchive (Map EntrySelector EntryDescription)
getEntries
case Maybe EntryDescription
mdesc of
Maybe EntryDescription
Nothing -> forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
throwM (FilePath -> EntrySelector -> ZipException
EntryDoesNotExist FilePath
path EntrySelector
s)
Just EntryDescription
desc -> forall (m :: * -> *) a. Monad m => a -> m a
return (forall (m :: * -> *).
(PrimMonad m, MonadThrow m, MonadResource m) =>
FilePath -> EntryDescription -> Bool -> ConduitT () ByteString m ()
I.sourceEntry FilePath
path EntryDescription
desc Bool
True)
sourceEntry ::
EntrySelector ->
ConduitT ByteString Void (ResourceT IO) a ->
ZipArchive a
sourceEntry :: forall a.
EntrySelector
-> ConduitT ByteString Void (ResourceT IO) a -> ZipArchive a
sourceEntry EntrySelector
s ConduitT ByteString Void (ResourceT IO) a
sink = do
ConduitT () ByteString (ResourceT IO) ()
src <- forall (m :: * -> *).
(PrimMonad m, MonadThrow m, MonadResource m) =>
EntrySelector -> ZipArchive (ConduitT () ByteString m ())
getEntrySource EntrySelector
s
(forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *) r.
MonadUnliftIO m =>
ConduitT () Void (ResourceT m) r -> m r
C.runConduitRes) (ConduitT () ByteString (ResourceT IO) ()
src forall (m :: * -> *) a b c r.
Monad m =>
ConduitT a b m () -> ConduitT b c m r -> ConduitT a c m r
.| ConduitT ByteString Void (ResourceT IO) a
sink)
saveEntry ::
EntrySelector ->
FilePath ->
ZipArchive ()
saveEntry :: EntrySelector -> FilePath -> ZipArchive ()
saveEntry EntrySelector
s FilePath
path = do
forall a.
EntrySelector
-> ConduitT ByteString Void (ResourceT IO) a -> ZipArchive a
sourceEntry EntrySelector
s (forall (m :: * -> *) o.
MonadResource m =>
FilePath -> ConduitT ByteString o m ()
CB.sinkFile FilePath
path)
Maybe EntryDescription
med <- EntrySelector -> ZipArchive (Maybe EntryDescription)
getEntryDesc EntrySelector
s
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ Maybe EntryDescription
med (forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> UTCTime -> IO ()
setModificationTime FilePath
path forall b c a. (b -> c) -> (a -> b) -> a -> c
. EntryDescription -> UTCTime
edModTime)
checkEntry ::
EntrySelector ->
ZipArchive Bool
checkEntry :: EntrySelector -> ZipArchive Bool
checkEntry EntrySelector
s = do
Word32
calculated <- forall a.
EntrySelector
-> ConduitT ByteString Void (ResourceT IO) a -> ZipArchive a
sourceEntry EntrySelector
s ConduitT ByteString Void (ResourceT IO) Word32
I.crc32Sink
Word32
given <- EntryDescription -> Word32
edCRC32 forall b c a. (b -> c) -> (a -> b) -> a -> c
. (forall k a. Ord k => Map k a -> k -> a
! EntrySelector
s) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ZipArchive (Map EntrySelector EntryDescription)
getEntries
forall (m :: * -> *) a. Monad m => a -> m a
return (Word32
calculated forall a. Eq a => a -> a -> Bool
== Word32
given)
unpackInto :: FilePath -> ZipArchive ()
unpackInto :: FilePath -> ZipArchive ()
unpackInto FilePath
dir' = do
Set EntrySelector
selectors <- forall k a. Map k a -> Set k
M.keysSet forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ZipArchive (Map EntrySelector EntryDescription)
getEntries
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (forall (t :: * -> *) a. Foldable t => t a -> Bool
null Set EntrySelector
selectors) forall a b. (a -> b) -> a -> b
$ do
FilePath
dir <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (FilePath -> IO FilePath
makeAbsolute FilePath
dir')
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (Bool -> FilePath -> IO ()
createDirectoryIfMissing Bool
True FilePath
dir)
let dirs :: Set FilePath
dirs = forall b a. Ord b => (a -> b) -> Set a -> Set b
E.map (FilePath -> FilePath
FP.takeDirectory forall b c a. (b -> c) -> (a -> b) -> a -> c
. (FilePath
dir FilePath -> FilePath -> FilePath
</>) forall b c a. (b -> c) -> (a -> b) -> a -> c
. EntrySelector -> FilePath
unEntrySelector) Set EntrySelector
selectors
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ Set FilePath
dirs (forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> FilePath -> IO ()
createDirectoryIfMissing Bool
True)
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ Set EntrySelector
selectors forall a b. (a -> b) -> a -> b
$ \EntrySelector
s ->
EntrySelector -> FilePath -> ZipArchive ()
saveEntry EntrySelector
s (FilePath
dir FilePath -> FilePath -> FilePath
</> EntrySelector -> FilePath
unEntrySelector EntrySelector
s)
getArchiveComment :: ZipArchive (Maybe Text)
= ArchiveDescription -> Maybe Text
adComment forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ZipArchive ArchiveDescription
getArchiveDescription
getArchiveDescription :: ZipArchive ArchiveDescription
getArchiveDescription :: ZipArchive ArchiveDescription
getArchiveDescription = forall a. StateT ZipState IO a -> ZipArchive a
ZipArchive (forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets ZipState -> ArchiveDescription
zsArchive)
addEntry ::
CompressionMethod ->
ByteString ->
EntrySelector ->
ZipArchive ()
addEntry :: CompressionMethod -> ByteString -> EntrySelector -> ZipArchive ()
addEntry CompressionMethod
t ByteString
b EntrySelector
s = PendingAction -> ZipArchive ()
addPending (CompressionMethod
-> ConduitT () ByteString (ResourceT IO) ()
-> EntrySelector
-> PendingAction
I.SinkEntry CompressionMethod
t (forall (m :: * -> *) o i. Monad m => o -> ConduitT i o m ()
C.yield ByteString
b) EntrySelector
s)
sinkEntry ::
CompressionMethod ->
ConduitT () ByteString (ResourceT IO) () ->
EntrySelector ->
ZipArchive ()
sinkEntry :: CompressionMethod
-> ConduitT () ByteString (ResourceT IO) ()
-> EntrySelector
-> ZipArchive ()
sinkEntry CompressionMethod
t ConduitT () ByteString (ResourceT IO) ()
src EntrySelector
s = PendingAction -> ZipArchive ()
addPending (CompressionMethod
-> ConduitT () ByteString (ResourceT IO) ()
-> EntrySelector
-> PendingAction
I.SinkEntry CompressionMethod
t ConduitT () ByteString (ResourceT IO) ()
src EntrySelector
s)
loadEntry ::
CompressionMethod ->
EntrySelector ->
FilePath ->
ZipArchive ()
loadEntry :: CompressionMethod -> EntrySelector -> FilePath -> ZipArchive ()
loadEntry CompressionMethod
t EntrySelector
s FilePath
path = do
FilePath
apath <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (FilePath -> IO FilePath
canonicalizePath FilePath
path)
UTCTime
modTime <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (FilePath -> IO UTCTime
getModificationTime FilePath
path)
let src :: ConduitT () ByteString (ResourceT IO) ()
src = forall (m :: * -> *) i.
MonadResource m =>
FilePath -> ConduitT i ByteString m ()
CB.sourceFile FilePath
apath
PendingAction -> ZipArchive ()
addPending (CompressionMethod
-> ConduitT () ByteString (ResourceT IO) ()
-> EntrySelector
-> PendingAction
I.SinkEntry CompressionMethod
t ConduitT () ByteString (ResourceT IO) ()
src EntrySelector
s)
PendingAction -> ZipArchive ()
addPending (UTCTime -> EntrySelector -> PendingAction
I.SetModTime UTCTime
modTime EntrySelector
s)
#ifndef mingw32_HOST_OS
FileStatus
status <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ FilePath -> IO FileStatus
Unix.getFileStatus FilePath
path
Word32 -> EntrySelector -> ZipArchive ()
setExternalFileAttrs (CMode -> Word32
Unix.fromFileMode (FileStatus -> CMode
Unix.fileMode FileStatus
status)) EntrySelector
s
#endif
copyEntry ::
FilePath ->
EntrySelector ->
EntrySelector ->
ZipArchive ()
copyEntry :: FilePath -> EntrySelector -> EntrySelector -> ZipArchive ()
copyEntry FilePath
path EntrySelector
s' EntrySelector
s = do
FilePath
apath <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (FilePath -> IO FilePath
canonicalizePath FilePath
path)
PendingAction -> ZipArchive ()
addPending (FilePath -> EntrySelector -> EntrySelector -> PendingAction
I.CopyEntry FilePath
apath EntrySelector
s' EntrySelector
s)
packDirRecur ::
CompressionMethod ->
(FilePath -> ZipArchive EntrySelector) ->
FilePath ->
ZipArchive ()
packDirRecur :: CompressionMethod
-> (FilePath -> ZipArchive EntrySelector)
-> FilePath
-> ZipArchive ()
packDirRecur CompressionMethod
t FilePath -> ZipArchive EntrySelector
f = CompressionMethod
-> (FilePath -> ZipArchive EntrySelector)
-> (EntrySelector -> ZipArchive ())
-> FilePath
-> ZipArchive ()
packDirRecur' CompressionMethod
t FilePath -> ZipArchive EntrySelector
f (forall a b. a -> b -> a
const forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. Monad m => a -> m a
return ())
packDirRecur' ::
CompressionMethod ->
(FilePath -> ZipArchive EntrySelector) ->
(EntrySelector -> ZipArchive ()) ->
FilePath ->
ZipArchive ()
packDirRecur' :: CompressionMethod
-> (FilePath -> ZipArchive EntrySelector)
-> (EntrySelector -> ZipArchive ())
-> FilePath
-> ZipArchive ()
packDirRecur' CompressionMethod
t FilePath -> ZipArchive EntrySelector
f EntrySelector -> ZipArchive ()
patch FilePath
path = do
[FilePath]
files <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (FilePath -> IO [FilePath]
listDirRecur FilePath
path)
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ [FilePath]
files forall a b. (a -> b) -> a -> b
$ \FilePath
x -> do
EntrySelector
s <- FilePath -> ZipArchive EntrySelector
f FilePath
x
CompressionMethod -> EntrySelector -> FilePath -> ZipArchive ()
loadEntry CompressionMethod
t EntrySelector
s (FilePath
path FilePath -> FilePath -> FilePath
</> FilePath
x)
EntrySelector -> ZipArchive ()
patch EntrySelector
s
renameEntry ::
EntrySelector ->
EntrySelector ->
ZipArchive ()
renameEntry :: EntrySelector -> EntrySelector -> ZipArchive ()
renameEntry EntrySelector
old EntrySelector
new = PendingAction -> ZipArchive ()
addPending (EntrySelector -> EntrySelector -> PendingAction
I.RenameEntry EntrySelector
old EntrySelector
new)
deleteEntry :: EntrySelector -> ZipArchive ()
deleteEntry :: EntrySelector -> ZipArchive ()
deleteEntry EntrySelector
s = PendingAction -> ZipArchive ()
addPending (EntrySelector -> PendingAction
I.DeleteEntry EntrySelector
s)
recompress ::
CompressionMethod ->
EntrySelector ->
ZipArchive ()
recompress :: CompressionMethod -> EntrySelector -> ZipArchive ()
recompress CompressionMethod
t EntrySelector
s = PendingAction -> ZipArchive ()
addPending (CompressionMethod -> EntrySelector -> PendingAction
I.Recompress CompressionMethod
t EntrySelector
s)
setEntryComment ::
Text ->
EntrySelector ->
ZipArchive ()
Text
text EntrySelector
s = PendingAction -> ZipArchive ()
addPending (Text -> EntrySelector -> PendingAction
I.SetEntryComment Text
text EntrySelector
s)
deleteEntryComment :: EntrySelector -> ZipArchive ()
EntrySelector
s = PendingAction -> ZipArchive ()
addPending (EntrySelector -> PendingAction
I.DeleteEntryComment EntrySelector
s)
setModTime ::
UTCTime ->
EntrySelector ->
ZipArchive ()
setModTime :: UTCTime -> EntrySelector -> ZipArchive ()
setModTime UTCTime
time EntrySelector
s = PendingAction -> ZipArchive ()
addPending (UTCTime -> EntrySelector -> PendingAction
I.SetModTime UTCTime
time EntrySelector
s)
addExtraField ::
Word16 ->
ByteString ->
EntrySelector ->
ZipArchive ()
Word16
n ByteString
b EntrySelector
s = PendingAction -> ZipArchive ()
addPending (Word16 -> ByteString -> EntrySelector -> PendingAction
I.AddExtraField Word16
n ByteString
b EntrySelector
s)
deleteExtraField ::
Word16 ->
EntrySelector ->
ZipArchive ()
Word16
n EntrySelector
s = PendingAction -> ZipArchive ()
addPending (Word16 -> EntrySelector -> PendingAction
I.DeleteExtraField Word16
n EntrySelector
s)
setExternalFileAttrs ::
Word32 ->
EntrySelector ->
ZipArchive ()
setExternalFileAttrs :: Word32 -> EntrySelector -> ZipArchive ()
setExternalFileAttrs Word32
attrs EntrySelector
s =
PendingAction -> ZipArchive ()
addPending (Word32 -> EntrySelector -> PendingAction
I.SetExternalFileAttributes Word32
attrs EntrySelector
s)
forEntries ::
(EntrySelector -> ZipArchive ()) ->
ZipArchive ()
forEntries :: (EntrySelector -> ZipArchive ()) -> ZipArchive ()
forEntries EntrySelector -> ZipArchive ()
action = ZipArchive (Map EntrySelector EntryDescription)
getEntries forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ EntrySelector -> ZipArchive ()
action forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall k a. Map k a -> Set k
M.keysSet
setArchiveComment :: Text -> ZipArchive ()
Text
text = PendingAction -> ZipArchive ()
addPending (Text -> PendingAction
I.SetArchiveComment Text
text)
deleteArchiveComment :: ZipArchive ()
= PendingAction -> ZipArchive ()
addPending PendingAction
I.DeleteArchiveComment
undoEntryChanges :: EntrySelector -> ZipArchive ()
undoEntryChanges :: EntrySelector -> ZipArchive ()
undoEntryChanges EntrySelector
s = (Seq PendingAction -> Seq PendingAction) -> ZipArchive ()
modifyActions Seq PendingAction -> Seq PendingAction
f
where
f :: Seq PendingAction -> Seq PendingAction
f = forall a. (a -> Bool) -> Seq a -> Seq a
S.filter ((forall a. Eq a => a -> a -> Bool
/= forall a. a -> Maybe a
Just EntrySelector
s) forall b c a. (b -> c) -> (a -> b) -> a -> c
. PendingAction -> Maybe EntrySelector
I.targetEntry)
undoArchiveChanges :: ZipArchive ()
undoArchiveChanges :: ZipArchive ()
undoArchiveChanges = (Seq PendingAction -> Seq PendingAction) -> ZipArchive ()
modifyActions Seq PendingAction -> Seq PendingAction
f
where
f :: Seq PendingAction -> Seq PendingAction
f = forall a. (a -> Bool) -> Seq a -> Seq a
S.filter ((forall a. Eq a => a -> a -> Bool
/= forall a. Maybe a
Nothing) forall b c a. (b -> c) -> (a -> b) -> a -> c
. PendingAction -> Maybe EntrySelector
I.targetEntry)
undoAll :: ZipArchive ()
undoAll :: ZipArchive ()
undoAll = (Seq PendingAction -> Seq PendingAction) -> ZipArchive ()
modifyActions (forall a b. a -> b -> a
const forall a. Seq a
S.empty)
commit :: ZipArchive ()
commit :: ZipArchive ()
commit = do
FilePath
file <- ZipArchive FilePath
getFilePath
ArchiveDescription
odesc <- ZipArchive ArchiveDescription
getArchiveDescription
Map EntrySelector EntryDescription
oentries <- ZipArchive (Map EntrySelector EntryDescription)
getEntries
Seq PendingAction
actions <- ZipArchive (Seq PendingAction)
getPending
Bool
exists <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (FilePath -> IO Bool
doesFileExist FilePath
file)
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (forall a. Seq a -> Bool
S.null Seq PendingAction
actions Bool -> Bool -> Bool
&& Bool
exists) forall a b. (a -> b) -> a -> b
$ do
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (FilePath
-> ArchiveDescription
-> Map EntrySelector EntryDescription
-> Seq PendingAction
-> IO ()
I.commit FilePath
file ArchiveDescription
odesc Map EntrySelector EntryDescription
oentries Seq PendingAction
actions)
(ArchiveDescription
ndesc, Map EntrySelector EntryDescription
nentries) <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (FilePath
-> IO (ArchiveDescription, Map EntrySelector EntryDescription)
I.scanArchive FilePath
file)
forall a. StateT ZipState IO a -> ZipArchive a
ZipArchive forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify forall a b. (a -> b) -> a -> b
$ \ZipState
st ->
ZipState
st
{ zsEntries :: Map EntrySelector EntryDescription
zsEntries = Map EntrySelector EntryDescription
nentries,
zsArchive :: ArchiveDescription
zsArchive = ArchiveDescription
ndesc,
zsActions :: Seq PendingAction
zsActions = forall a. Seq a
S.empty
}
getFilePath :: ZipArchive FilePath
getFilePath :: ZipArchive FilePath
getFilePath = forall a. StateT ZipState IO a -> ZipArchive a
ZipArchive (forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets ZipState -> FilePath
zsFilePath)
getPending :: ZipArchive (Seq I.PendingAction)
getPending :: ZipArchive (Seq PendingAction)
getPending = forall a. StateT ZipState IO a -> ZipArchive a
ZipArchive (forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets ZipState -> Seq PendingAction
zsActions)
modifyActions :: (Seq I.PendingAction -> Seq I.PendingAction) -> ZipArchive ()
modifyActions :: (Seq PendingAction -> Seq PendingAction) -> ZipArchive ()
modifyActions Seq PendingAction -> Seq PendingAction
f = forall a. StateT ZipState IO a -> ZipArchive a
ZipArchive (forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify ZipState -> ZipState
g)
where
g :: ZipState -> ZipState
g ZipState
st = ZipState
st {zsActions :: Seq PendingAction
zsActions = Seq PendingAction -> Seq PendingAction
f (ZipState -> Seq PendingAction
zsActions ZipState
st)}
addPending :: I.PendingAction -> ZipArchive ()
addPending :: PendingAction -> ZipArchive ()
addPending PendingAction
a = (Seq PendingAction -> Seq PendingAction) -> ZipArchive ()
modifyActions (forall a. Seq a -> a -> Seq a
|> PendingAction
a)
listDirRecur :: FilePath -> IO [FilePath]
listDirRecur :: FilePath -> IO [FilePath]
listDirRecur FilePath
path = forall a. DList a -> [a]
DList.toList forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> FilePath -> IO (DList FilePath)
go FilePath
""
where
go :: FilePath -> IO (DList FilePath)
go FilePath
adir = do
let cdir :: FilePath
cdir = FilePath
path FilePath -> FilePath -> FilePath
</> FilePath
adir
[FilePath]
raw <- FilePath -> IO [FilePath]
listDirectory FilePath
cdir
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. Monoid a => [a] -> a
mconcat forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
t a -> (a -> m b) -> m (t b)
forM [FilePath]
raw forall a b. (a -> b) -> a -> b
$ \case
FilePath
"" -> forall (m :: * -> *) a. Monad m => a -> m a
return forall a. Monoid a => a
mempty
FilePath
"." -> forall (m :: * -> *) a. Monad m => a -> m a
return forall a. Monoid a => a
mempty
FilePath
".." -> forall (m :: * -> *) a. Monad m => a -> m a
return forall a. Monoid a => a
mempty
FilePath
x -> do
let fullx :: FilePath
fullx = FilePath
cdir FilePath -> FilePath -> FilePath
</> FilePath
x
adir' :: FilePath
adir' = FilePath
adir FilePath -> FilePath -> FilePath
</> FilePath
x
Bool
isFile <- FilePath -> IO Bool
doesFileExist FilePath
fullx
Bool
isDir <- FilePath -> IO Bool
doesDirectoryExist FilePath
fullx
if Bool
isFile
then forall (m :: * -> *) a. Monad m => a -> m a
return (forall a. a -> DList a
DList.singleton FilePath
adir')
else
if Bool
isDir
then FilePath -> IO (DList FilePath)
go FilePath
adir'
else forall (m :: * -> *) a. Monad m => a -> m a
return forall a. Monoid a => a
mempty
ignoringAbsence :: IO () -> IO ()
ignoringAbsence :: IO () -> IO ()
ignoringAbsence IO ()
io = forall (m :: * -> *) e b a.
(MonadCatch m, Exception e) =>
(e -> Maybe b) -> m a -> (b -> m a) -> m a
catchJust IOError -> Maybe IOError
select IO ()
io forall {b}. b -> IO ()
handler
where
select :: IOError -> Maybe IOError
select IOError
e = if IOError -> Bool
isDoesNotExistError IOError
e then forall a. a -> Maybe a
Just IOError
e else forall a. Maybe a
Nothing
handler :: b -> IO ()
handler = forall a b. a -> b -> a
const (forall (m :: * -> *) a. Monad m => a -> m a
return ())