{-# LANGUAGE CPP #-}

#if defined(mingw32_HOST_OS) || defined(__MINGW32__)

{-# LANGUAGE ForeignFunctionInterface #-}

#if defined(i386_HOST_ARCH)
#define WINDOWS_CCONV stdcall
#elif defined(x86_64_HOST_ARCH)
#define WINDOWS_CCONV ccall
#else
#error Unknown mingw32 arch
#endif

#endif


{- |
Compatibility code for things that need to be done differently
on different systems.
-}
module Config.Dyre.Compat ( customExec, getPIDString ) where

import Config.Dyre.Options ( customOptions )

#if defined(mingw32_HOST_OS) || defined(__MINGW32__)

-- Windows

import System.Win32
import System.Process
import System.Exit
import System.Mem

-- This can be removed as soon as a 'getProcessID' function
-- gets added to 'System.Win32'
foreign import WINDOWS_CCONV unsafe "winbase.h GetCurrentProcessId"
    c_GetCurrentProcessID :: IO DWORD
getPIDString = fmap show c_GetCurrentProcessID

customExec binary mArgs = do
    args <- customOptions mArgs
    -- This whole thing is a terrible, ugly hack. Since Windows
    -- is too braindead to provide an exec() system call for us
    -- to use, we simply create a new process that inherits
    -- the stdio handles.
    (_,_,_,child) <- createProcess $ proc binary args

    -- Do some garbage collection in an optimistic attempt to
    -- offset some of the memory we waste here.
    performGC

    -- And to prevent terminal apps from losing IO, we have to
    -- sit around waiting for the child to exit.
    --
    -- 'exitWith' will flush stdout and stderr
    waitForProcess child >>= exitWith

#else

import System.Posix.Process ( executeFile, getProcessID )

#ifdef darwin_HOST_OS

import System.Posix.Process
  ( exitImmediately , forkProcess, getProcessStatus, ProcessStatus(..) )
import System.Posix.Signals ( raiseSignal, sigTSTP )
import System.Exit          ( ExitCode(ExitSuccess) )

-- OSX.  In a threaded process execv fails with ENOTSUP.
-- See http://uninformed.org/index.cgi?v=1&a=1&p=16.  So it
-- is necessary to fork _then_ exec.
--
-- According to https://bugs.python.org/issue6800 this was
-- fixed in OS X 10.6.  But I guess we'll leave the workaround
-- in place until there is a compelling reason to remove it.

customExec binary mArgs = do
    args <- customOptions mArgs
    childPID <- forkProcess $ executeFile binary False args Nothing
    forever $ do
        childStatus <- getProcessStatus True True childPID
        case childStatus of
             Nothing -> error "executeFile: couldn't get child process status"
             Just (Exited code) -> exitImmediately code
#if MIN_VERSION_unix(2,7,0)
             Just (Terminated _ _) -> exitImmediately ExitSuccess
#else
             Just (Terminated _) -> exitImmediately ExitSuccess
#endif
             Just (Stopped _) -> raiseSignal sigTSTP
  where forever a = a >> forever a

#else

-- Linux / BSD

customExec :: FilePath -> Maybe [FilePath] -> IO a
customExec FilePath
binary Maybe [FilePath]
mArgs = do
   [FilePath]
args <- Maybe [FilePath] -> IO [FilePath]
customOptions Maybe [FilePath]
mArgs
   FilePath
-> Bool -> [FilePath] -> Maybe [(FilePath, FilePath)] -> IO a
forall a.
FilePath
-> Bool -> [FilePath] -> Maybe [(FilePath, FilePath)] -> IO a
executeFile FilePath
binary Bool
False [FilePath]
args Maybe [(FilePath, FilePath)]
forall a. Maybe a
Nothing

#endif

getPIDString :: IO FilePath
getPIDString = (ProcessID -> FilePath) -> IO ProcessID -> IO FilePath
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ProcessID -> FilePath
forall a. Show a => a -> FilePath
show IO ProcessID
getProcessID

#endif


-- | Called whenever execution needs to be transferred over to
--   a different binary.
customExec :: FilePath -> Maybe [String] -> IO a

-- | What it says on the tin. Gets the current PID as a string.
--   Used to determine the name for the state file during restarts.
getPIDString :: IO String