{-# LANGUAGE CPP #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module System.IO.Temp.OsPath (
withSystemTempFile,
withSystemTempDirectory,
withTempFile,
withTempDirectory,
openNewBinaryFile,
createTempDirectory,
writeTempFile,
writeSystemTempFile,
emptyTempFile,
emptySystemTempFile,
createTempFileName,
withTempFileName,
openTempFile,
openBinaryTempFile,
getCanonicalTemporaryDirectory,
) where
import qualified Control.Monad.Catch as MC
import Control.Monad.IO.Class (MonadIO, liftIO)
import Data.Bits (countLeadingZeros, shiftR)
import Data.ByteString.Lazy (LazyByteString)
import qualified Data.ByteString.Lazy as BL
import Data.Word (Word64)
import GHC.IORef (IORef, atomicModifyIORef'_, newIORef)
import Numeric (showHex)
import System.CPUTime (cpuTimePrecision, getCPUTime)
import System.Directory.OsPath (
canonicalizePath,
getTemporaryDirectory,
removeDirectoryRecursive,
removeFile,
)
import System.File.OsPath (
openBinaryTempFile,
openBinaryTempFileWithDefaultPermissions,
openTempFile,
)
import System.IO (
Handle,
hClose,
)
import System.IO.Error (isAlreadyExistsError)
import System.IO.Unsafe (unsafePerformIO)
import System.OsPath (
OsPath,
OsString,
encodeFS,
isPathSeparator,
makeValid,
osp,
(</>),
)
import System.OsString (filter)
import System.Posix.Internals (c_getpid)
import Prelude hiding (filter)
#ifdef mingw32_HOST_OS
import System.Directory.OsPath (createDirectory)
#else
import qualified System.Posix.Directory.PosixPath
import System.OsString.Internal.Types (getOsString)
#endif
withSystemTempFile
:: (MonadIO m, MC.MonadMask m)
=> OsString
-> (OsPath -> Handle -> m a)
-> m a
withSystemTempFile :: forall (m :: * -> *) a.
(MonadIO m, MonadMask m) =>
OsPath -> (OsPath -> Handle -> m a) -> m a
withSystemTempFile OsPath
template OsPath -> Handle -> m a
action = do
OsPath
tmpDir <- IO OsPath -> m OsPath
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO OsPath
getCanonicalTemporaryDirectory
OsPath -> OsPath -> (OsPath -> Handle -> m a) -> m a
forall (m :: * -> *) a.
(MonadIO m, MonadMask m) =>
OsPath -> OsPath -> (OsPath -> Handle -> m a) -> m a
withTempFile OsPath
tmpDir OsPath
template OsPath -> Handle -> m a
action
withSystemTempDirectory
:: (MonadIO m, MC.MonadMask m)
=> OsString
-> (OsPath -> m a)
-> m a
withSystemTempDirectory :: forall (m :: * -> *) a.
(MonadIO m, MonadMask m) =>
OsPath -> (OsPath -> m a) -> m a
withSystemTempDirectory OsPath
template OsPath -> m a
action = do
OsPath
tmpDir <- IO OsPath -> m OsPath
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO OsPath
getCanonicalTemporaryDirectory
OsPath -> OsPath -> (OsPath -> m a) -> m a
forall (m :: * -> *) a.
(MonadMask m, MonadIO m) =>
OsPath -> OsPath -> (OsPath -> m a) -> m a
withTempDirectory OsPath
tmpDir OsPath
template OsPath -> m a
action
withTempFile
:: (MonadIO m, MC.MonadMask m)
=> OsPath
-> OsString
-> (OsPath -> Handle -> m a)
-> m a
withTempFile :: forall (m :: * -> *) a.
(MonadIO m, MonadMask m) =>
OsPath -> OsPath -> (OsPath -> Handle -> m a) -> m a
withTempFile OsPath
tmpDir OsPath
template OsPath -> Handle -> m a
action =
m (OsPath, Handle)
-> ((OsPath, Handle) -> m ()) -> ((OsPath, Handle) -> m a) -> m a
forall (m :: * -> *) a c b.
(HasCallStack, MonadMask m) =>
m a -> (a -> m c) -> (a -> m b) -> m b
MC.bracket
(IO (OsPath, Handle) -> m (OsPath, Handle)
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (OsPath -> OsPath -> IO (OsPath, Handle)
openTempFile OsPath
tmpDir OsPath
template))
(\(OsPath
name, Handle
handle) -> IO () -> m ()
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (Handle -> IO ()
hClose Handle
handle IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> IO () -> IO ()
forall (m :: * -> *). MonadCatch m => m () -> m ()
ignoringIOErrors (OsPath -> IO ()
removeFile OsPath
name)))
((OsPath -> Handle -> m a) -> (OsPath, Handle) -> m a
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry OsPath -> Handle -> m a
action)
withTempDirectory
:: (MC.MonadMask m, MonadIO m)
=> OsPath
-> OsString
-> (OsPath -> m a)
-> m a
withTempDirectory :: forall (m :: * -> *) a.
(MonadMask m, MonadIO m) =>
OsPath -> OsPath -> (OsPath -> m a) -> m a
withTempDirectory OsPath
targetDir OsPath
template =
m OsPath -> (OsPath -> m ()) -> (OsPath -> m a) -> m a
forall (m :: * -> *) a c b.
(HasCallStack, MonadMask m) =>
m a -> (a -> m c) -> (a -> m b) -> m b
MC.bracket
(IO OsPath -> m OsPath
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (OsPath -> OsPath -> IO OsPath
createTempDirectory OsPath
targetDir OsPath
template))
(IO () -> m ()
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> (OsPath -> IO ()) -> OsPath -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IO () -> IO ()
forall (m :: * -> *). MonadCatch m => m () -> m ()
ignoringIOErrors (IO () -> IO ()) -> (OsPath -> IO ()) -> OsPath -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. OsPath -> IO ()
removeDirectoryRecursive)
writeTempFile
:: OsPath
-> OsString
-> LazyByteString
-> IO OsPath
writeTempFile :: OsPath -> OsPath -> LazyByteString -> IO OsPath
writeTempFile OsPath
targetDir OsPath
template LazyByteString
content =
IO (OsPath, Handle)
-> ((OsPath, Handle) -> IO ())
-> ((OsPath, Handle) -> IO OsPath)
-> IO OsPath
forall (m :: * -> *) a c b.
(HasCallStack, MonadMask m) =>
m a -> (a -> m c) -> (a -> m b) -> m b
MC.bracket
(OsPath -> OsPath -> IO (OsPath, Handle)
openBinaryTempFile OsPath
targetDir OsPath
template)
(\(OsPath
_, Handle
handle) -> Handle -> IO ()
hClose Handle
handle)
(\(OsPath
filePath, Handle
handle) -> Handle -> LazyByteString -> IO ()
BL.hPut Handle
handle LazyByteString
content IO () -> IO OsPath -> IO OsPath
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> OsPath -> IO OsPath
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure OsPath
filePath)
writeSystemTempFile
:: OsString
-> LazyByteString
-> IO OsPath
writeSystemTempFile :: OsPath -> LazyByteString -> IO OsPath
writeSystemTempFile OsPath
template LazyByteString
content = do
OsPath
tmpDir <- IO OsPath
getCanonicalTemporaryDirectory
OsPath -> OsPath -> LazyByteString -> IO OsPath
writeTempFile OsPath
tmpDir OsPath
template LazyByteString
content
emptyTempFile
:: OsPath
-> OsString
-> IO OsPath
emptyTempFile :: OsPath -> OsPath -> IO OsPath
emptyTempFile OsPath
targetDir OsPath
template =
IO (OsPath, Handle)
-> ((OsPath, Handle) -> IO ())
-> ((OsPath, Handle) -> IO OsPath)
-> IO OsPath
forall (m :: * -> *) a c b.
(HasCallStack, MonadMask m) =>
m a -> (a -> m c) -> (a -> m b) -> m b
MC.bracket
(OsPath -> OsPath -> IO (OsPath, Handle)
openBinaryTempFile OsPath
targetDir OsPath
template)
(\(OsPath
_, Handle
handle) -> Handle -> IO ()
hClose Handle
handle)
(\(OsPath
filePath, Handle
_) -> OsPath -> IO OsPath
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure OsPath
filePath)
emptySystemTempFile
:: OsString
-> IO OsPath
emptySystemTempFile :: OsPath -> IO OsPath
emptySystemTempFile OsPath
template = do
OsPath
tmpDir <- IO OsPath
getCanonicalTemporaryDirectory
OsPath -> OsPath -> IO OsPath
emptyTempFile OsPath
tmpDir OsPath
template
ignoringIOErrors :: MC.MonadCatch m => m () -> m ()
ignoringIOErrors :: forall (m :: * -> *). MonadCatch m => m () -> m ()
ignoringIOErrors m ()
ioe = m ()
ioe m () -> (IOError -> m ()) -> m ()
forall e a. (HasCallStack, Exception e) => m a -> (e -> m a) -> m a
forall (m :: * -> *) e a.
(MonadCatch m, HasCallStack, Exception e) =>
m a -> (e -> m a) -> m a
`MC.catch` (\(IOError
_ :: IOError) -> () -> m ()
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ())
openNewBinaryFile :: OsPath -> OsString -> IO (OsPath, Handle)
openNewBinaryFile :: OsPath -> OsPath -> IO (OsPath, Handle)
openNewBinaryFile = OsPath -> OsPath -> IO (OsPath, Handle)
openBinaryTempFileWithDefaultPermissions
{-# DEPRECATED openNewBinaryFile "Use 'openBinaryTempFileWithDefaultPermissions' instead" #-}
createTempDirectory
:: OsPath
-> OsString
-> IO OsPath
createTempDirectory :: OsPath -> OsPath -> IO OsPath
createTempDirectory OsPath
dir OsPath
template = IO OsPath
findTempName
where
findTempName :: IO OsPath
findTempName :: IO OsPath
findTempName = do
OsPath
dirpath <- (OsPath
dir OsPath -> OsPath -> OsPath
</>) (OsPath -> OsPath) -> IO OsPath -> IO OsPath
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> OsPath -> IO OsPath
randomString (OsPath -> OsPath
sanitizeAsDirName OsPath
template)
Either IOError ()
r <- IO () -> IO (Either IOError ())
forall (m :: * -> *) e a.
(HasCallStack, MonadCatch m, Exception e) =>
m a -> m (Either e a)
MC.try (IO () -> IO (Either IOError ()))
-> IO () -> IO (Either IOError ())
forall a b. (a -> b) -> a -> b
$ OsPath -> IO ()
mkPrivateDir OsPath
dirpath
case Either IOError ()
r of
Right ()
_ -> OsPath -> IO OsPath
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure OsPath
dirpath
Left IOError
e
| IOError -> Bool
isAlreadyExistsError IOError
e -> IO OsPath
findTempName
| Bool
otherwise -> IOError -> IO OsPath
forall a. IOError -> IO a
ioError IOError
e
tempDirectoryCounter :: IORef Word
tempDirectoryCounter :: IORef Word
tempDirectoryCounter = IO (IORef Word) -> IORef Word
forall a. IO a -> a
unsafePerformIO (IO (IORef Word) -> IORef Word) -> IO (IORef Word) -> IORef Word
forall a b. (a -> b) -> a -> b
$ Word -> IO (IORef Word)
forall a. a -> IO (IORef a)
newIORef Word
0
{-# NOINLINE tempDirectoryCounter #-}
randomString :: OsString -> IO OsPath
randomString :: OsPath -> IO OsPath
randomString OsPath
template = do
CPid
r1 <- IO CPid
c_getpid
(Word
r2, Word
_) <- IORef Word -> (Word -> Word) -> IO (Word, Word)
forall a. IORef a -> (a -> a) -> IO (a, a)
atomicModifyIORef'_ IORef Word
tempDirectoryCounter (Word -> Word -> Word
forall a. Num a => a -> a -> a
+ Word
1)
Integer
r3 <- (Integer -> Int -> Integer
forall a. Bits a => a -> Int -> a
`shiftR` Int
logCpuTimePrecision) (Integer -> Integer) -> IO Integer -> IO Integer
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO Integer
getCPUTime
let suffix :: String
suffix = CPid -> ShowS
forall a. Integral a => a -> ShowS
showHex (CPid -> CPid
forall a. Num a => a -> a
abs CPid
r1) (Char
'-' Char -> ShowS
forall a. a -> [a] -> [a]
: Word -> ShowS
forall a. Integral a => a -> ShowS
showHex Word
r2 (Char
'-' Char -> ShowS
forall a. a -> [a] -> [a]
: Integer -> ShowS
forall a. Integral a => a -> ShowS
showHex (Integer -> Integer
forall a. Num a => a -> a
abs Integer
r3) String
""))
OsPath -> IO OsPath
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (OsPath -> IO OsPath) -> OsPath -> IO OsPath
forall a b. (a -> b) -> a -> b
$ OsPath
template OsPath -> OsPath -> OsPath
forall a. Semigroup a => a -> a -> a
<> IO OsPath -> OsPath
forall a. IO a -> a
unsafePerformIO (String -> IO OsPath
encodeFS String
suffix)
#ifdef netbsd_HOST_OS
logCpuTimePrecision :: Int
logCpuTimePrecision = 0
#else
logCpuTimePrecision :: Int
logCpuTimePrecision :: Int
logCpuTimePrecision =
Int
64 Int -> Int -> Int
forall a. Num a => a -> a -> a
- Word64 -> Int
forall b. FiniteBits b => b -> Int
countLeadingZeros (Integer -> Word64
forall a. Num a => Integer -> a
fromInteger Integer
cpuTimePrecision :: Word64)
#endif
sanitizeAsDirName :: OsString -> OsPath
sanitizeAsDirName :: OsPath -> OsPath
sanitizeAsDirName = OsPath -> OsPath
makeValid (OsPath -> OsPath) -> (OsPath -> OsPath) -> OsPath -> OsPath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (OsChar -> Bool) -> OsPath -> OsPath
filter (\OsChar
c -> Bool -> Bool
not (OsChar -> Bool
isPathSeparator OsChar
c))
createTempFileName
:: OsPath
-> OsString
-> IO OsPath
createTempFileName :: OsPath -> OsPath -> IO OsPath
createTempFileName OsPath
dir OsPath
template = do
OsPath
tempDir <- OsPath -> OsPath -> IO OsPath
createTempDirectory OsPath
dir OsPath
template
OsPath -> IO OsPath
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (OsPath
tempDir OsPath -> OsPath -> OsPath
</> [osp|temp_file|])
withTempFileName
:: (MonadIO m, MC.MonadMask m)
=> OsPath
-> OsString
-> (OsPath -> m a)
-> m a
withTempFileName :: forall (m :: * -> *) a.
(MonadIO m, MonadMask m) =>
OsPath -> OsPath -> (OsPath -> m a) -> m a
withTempFileName OsPath
dir OsPath
template OsPath -> m a
k = OsPath -> OsPath -> (OsPath -> m a) -> m a
forall (m :: * -> *) a.
(MonadMask m, MonadIO m) =>
OsPath -> OsPath -> (OsPath -> m a) -> m a
withTempDirectory OsPath
dir OsPath
template ((OsPath -> m a) -> m a) -> (OsPath -> m a) -> m a
forall a b. (a -> b) -> a -> b
$ \OsPath
tempDir ->
OsPath -> m a
k (OsPath
tempDir OsPath -> OsPath -> OsPath
</> [osp|temp_file|])
mkPrivateDir :: OsPath -> IO ()
#ifdef mingw32_HOST_OS
mkPrivateDir = createDirectory
#else
mkPrivateDir :: OsPath -> IO ()
mkPrivateDir OsPath
s =
PosixString -> FileMode -> IO ()
System.Posix.Directory.PosixPath.createDirectory (OsPath -> PosixString
getOsString OsPath
s) FileMode
0o700
#endif
getCanonicalTemporaryDirectory :: IO OsPath
getCanonicalTemporaryDirectory :: IO OsPath
getCanonicalTemporaryDirectory = IO OsPath
getTemporaryDirectory IO OsPath -> (OsPath -> IO OsPath) -> IO OsPath
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= OsPath -> IO OsPath
canonicalizePath