{-# LANGUAGE CPP #-}
{-# OPTIONS_HADDOCK hide #-}

module Distribution.Compat.Internal.TempFile
  ( openTempFile
  , openBinaryTempFile
  , openNewBinaryFile
  , createTempDirectory
  ) where

import Distribution.Compat.Exception

import GHC.IORef (IORef, atomicModifyIORef'_, newIORef)
import System.FilePath ((</>))
import System.IO (Handle, openBinaryTempFile, openBinaryTempFileWithDefaultPermissions, openTempFile)
import System.IO.Error (isAlreadyExistsError)
import System.IO.Unsafe (unsafePerformIO)
import System.Posix.Internals (c_getpid)

#if defined(mingw32_HOST_OS) || defined(ghcjs_HOST_OS)
import System.Directory       ( createDirectory )
#else
import qualified System.Posix
#endif

openNewBinaryFile :: FilePath -> String -> IO (FilePath, Handle)
openNewBinaryFile :: FilePath -> FilePath -> IO (FilePath, Handle)
openNewBinaryFile = FilePath -> FilePath -> IO (FilePath, Handle)
openBinaryTempFileWithDefaultPermissions

createTempDirectory :: FilePath -> String -> IO FilePath
createTempDirectory :: FilePath -> FilePath -> IO FilePath
createTempDirectory FilePath
dir FilePath
template = do
  pid <- IO CPid
c_getpid
  let findTempName = do
        (counter, _) <- 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)
        let relpath = FilePath
template FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
"-" FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ CPid -> FilePath
forall a. Show a => a -> FilePath
show CPid
pid FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ Word -> FilePath
forall a. Show a => a -> FilePath
show Word
counter
            dirpath = FilePath
dir FilePath -> FilePath -> FilePath
</> FilePath
relpath
        r <- tryIO $ mkPrivateDir dirpath
        case r of
          Right ()
_ -> FilePath -> IO FilePath
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure FilePath
relpath
          Left IOException
e
            | IOException -> Bool
isAlreadyExistsError IOException
e -> IO FilePath
findTempName
            | Bool
otherwise -> IOException -> IO FilePath
forall a. HasCallStack => IOException -> IO a
ioError IOException
e
  findTempName

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 #-}

mkPrivateDir :: String -> IO ()
#if defined(mingw32_HOST_OS) || defined(ghcjs_HOST_OS)
mkPrivateDir s = createDirectory s
#else
mkPrivateDir :: FilePath -> IO ()
mkPrivateDir FilePath
s = FilePath -> FileMode -> IO ()
System.Posix.createDirectory FilePath
s FileMode
0o700
#endif