{-# LANGUAGE CPP #-}
{-# OPTIONS_HADDOCK hide #-}
module Distribution.Compat.CopyFile
( copyFile
, copyFileChanged
, filesEqual
, copyOrdinaryFile
, copyExecutableFile
, setFileOrdinary
, setFileExecutable
, setDirOrdinary
) where
import Distribution.Compat.Prelude
import Prelude ()
import qualified Data.ByteString.Lazy as BSL
import System.Directory
( doesFileExist
)
import System.IO
( IOMode (ReadMode)
, hFileSize
, withBinaryFile
)
#ifndef mingw32_HOST_OS
import Control.Exception
( bracketOnError )
import Data.Bits
( (.|.) )
import System.IO.Error
( ioeSetLocation )
import System.Directory
( renameFile, removeFile )
import System.FilePath
( takeDirectory )
import System.IO
( hClose, hGetBuf, hPutBuf, openBinaryTempFile )
import Foreign
( allocaBytes )
import System.Posix.Types
( FileMode )
import System.Posix.Files
( getFileStatus, fileMode, setFileMode )
#else /* else mingw32_HOST_OS */
import System.Directory
( copyFileWithMetadata )
#endif /* mingw32_HOST_OS */
copyOrdinaryFile, copyExecutableFile :: FilePath -> FilePath -> IO ()
copyOrdinaryFile :: FilePath -> FilePath -> IO ()
copyOrdinaryFile FilePath
src FilePath
dest = FilePath -> FilePath -> IO ()
copyFile FilePath
src FilePath
dest IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> FilePath -> IO ()
setFileOrdinary FilePath
dest
copyExecutableFile :: FilePath -> FilePath -> IO ()
copyExecutableFile FilePath
src FilePath
dest = FilePath -> FilePath -> IO ()
copyFile FilePath
src FilePath
dest IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> FilePath -> IO ()
setFileExecutable FilePath
dest
setFileOrdinary, setFileExecutable, setDirOrdinary :: FilePath -> IO ()
#ifndef mingw32_HOST_OS
setFileOrdinary :: FilePath -> IO ()
setFileOrdinary FilePath
path = FilePath -> FileMode -> IO ()
addFileMode FilePath
path FileMode
0o644
setFileExecutable :: FilePath -> IO ()
setFileExecutable FilePath
path = FilePath -> FileMode -> IO ()
addFileMode FilePath
path FileMode
0o755
addFileMode :: FilePath -> FileMode -> IO ()
addFileMode :: FilePath -> FileMode -> IO ()
addFileMode FilePath
name FileMode
m = do
o <- FileStatus -> FileMode
fileMode (FileStatus -> FileMode) -> IO FileStatus -> IO FileMode
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> FilePath -> IO FileStatus
getFileStatus FilePath
name
setFileMode name (m .|. o)
#else
setFileOrdinary _ = return ()
setFileExecutable _ = return ()
#endif
setDirOrdinary :: FilePath -> IO ()
setDirOrdinary = FilePath -> IO ()
setFileExecutable
copyFile :: FilePath -> FilePath -> IO ()
#ifndef mingw32_HOST_OS
copyFile :: FilePath -> FilePath -> IO ()
copyFile FilePath
fromFPath FilePath
toFPath =
IO ()
copy
IO () -> (IOError -> IO ()) -> IO ()
forall a. IO a -> (IOError -> IO a) -> IO a
`catchIO` (\IOError
ioe -> IOError -> IO ()
forall e a. (HasCallStack, Exception e) => e -> IO a
throwIO (IOError -> FilePath -> IOError
ioeSetLocation IOError
ioe FilePath
"copyFile"))
where
copy :: IO ()
copy = FilePath -> IOMode -> (Handle -> IO ()) -> IO ()
forall r. FilePath -> IOMode -> (Handle -> IO r) -> IO r
withBinaryFile FilePath
fromFPath IOMode
ReadMode ((Handle -> IO ()) -> IO ()) -> (Handle -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \Handle
hFrom ->
IO (FilePath, Handle)
-> ((FilePath, Handle) -> IO ())
-> ((FilePath, Handle) -> IO ())
-> IO ()
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
bracketOnError IO (FilePath, Handle)
openTmp (FilePath, Handle) -> IO ()
cleanTmp (((FilePath, Handle) -> IO ()) -> IO ())
-> ((FilePath, Handle) -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \(FilePath
tmpFPath, Handle
hTmp) ->
do Int -> (Ptr (ZonkAny 0) -> IO ()) -> IO ()
forall a b. Int -> (Ptr a -> IO b) -> IO b
allocaBytes Int
bufferSize ((Ptr (ZonkAny 0) -> IO ()) -> IO ())
-> (Ptr (ZonkAny 0) -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ Handle -> Handle -> Ptr (ZonkAny 0) -> IO ()
forall {a}. Handle -> Handle -> Ptr a -> IO ()
copyContents Handle
hFrom Handle
hTmp
Handle -> IO ()
hClose Handle
hTmp
FilePath -> FilePath -> IO ()
renameFile FilePath
tmpFPath FilePath
toFPath
openTmp :: IO (FilePath, Handle)
openTmp = FilePath -> FilePath -> IO (FilePath, Handle)
openBinaryTempFile (FilePath -> FilePath
takeDirectory FilePath
toFPath) FilePath
".copyFile.tmp"
cleanTmp :: (FilePath, Handle) -> IO ()
cleanTmp (FilePath
tmpFPath, Handle
hTmp) = do
Handle -> IO ()
hClose Handle
hTmp IO () -> (IOError -> IO ()) -> IO ()
forall a. IO a -> (IOError -> IO a) -> IO a
`catchIO` \IOError
_ -> () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
FilePath -> IO ()
removeFile FilePath
tmpFPath IO () -> (IOError -> IO ()) -> IO ()
forall a. IO a -> (IOError -> IO a) -> IO a
`catchIO` \IOError
_ -> () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
bufferSize :: Int
bufferSize = Int
4096
copyContents :: Handle -> Handle -> Ptr a -> IO ()
copyContents Handle
hFrom Handle
hTo Ptr a
buffer = do
count <- Handle -> Ptr a -> Int -> IO Int
forall a. Handle -> Ptr a -> Int -> IO Int
hGetBuf Handle
hFrom Ptr a
buffer Int
bufferSize
when (count > 0) $ do
hPutBuf hTo buffer count
copyContents hFrom hTo buffer
#else
copyFile = copyFileWithMetadata
#endif /* mingw32_HOST_OS */
copyFileChanged :: FilePath -> FilePath -> IO ()
copyFileChanged :: FilePath -> FilePath -> IO ()
copyFileChanged FilePath
src FilePath
dest = do
equal <- FilePath -> FilePath -> IO Bool
filesEqual FilePath
src FilePath
dest
unless equal $ copyFile src dest
filesEqual :: FilePath -> FilePath -> IO Bool
filesEqual :: FilePath -> FilePath -> IO Bool
filesEqual FilePath
f1 FilePath
f2 = do
ex1 <- FilePath -> IO Bool
doesFileExist FilePath
f1
ex2 <- doesFileExist f2
if not (ex1 && ex2)
then return False
else withBinaryFile f1 ReadMode $ \Handle
h1 ->
FilePath -> IOMode -> (Handle -> IO Bool) -> IO Bool
forall r. FilePath -> IOMode -> (Handle -> IO r) -> IO r
withBinaryFile FilePath
f2 IOMode
ReadMode ((Handle -> IO Bool) -> IO Bool) -> (Handle -> IO Bool) -> IO Bool
forall a b. (a -> b) -> a -> b
$ \Handle
h2 -> do
s1 <- Handle -> IO Integer
hFileSize Handle
h1
s2 <- hFileSize h2
if s1 /= s2
then return False
else do
c1 <- BSL.hGetContents h1
c2 <- BSL.hGetContents h2
return $! c1 == c2