{-# LANGUAGE CPP #-}
--
-- | A Posix.popen compatibility mapping.
--
-- If we use this, we should build -threaded
--
module System.Plugins.Process (exec, popen) where

import System.Exit
#if __GLASGOW_HASKELL__ >= 604
import System.IO
import System.Process
import Control.Concurrent       (forkIO)
#else
import qualified Posix as P
#endif

import qualified Control.Exception as E

--
-- slight wrapper over popen for calls that don't care about stdin to the program
--
exec :: String -> [String] -> IO ([String],[String])
exec f as = do
        (a,b,_) <- popen f as (Just [])
        return (lines a, lines b)

#if __GLASGOW_HASKELL__ >= 604

type ProcessID = ProcessHandle

--
-- Ignoring exit status for now.
--
-- XXX there are still issues. Large amounts of output can cause what
-- seems to be a dead lock on the pipe write from runplugs, for example.
-- Posix.popen doesn't have this problem, so maybe we can reproduce its
-- pipe handling somehow.
--
popen :: FilePath -> [String] -> Maybe String -> IO (String,String,ProcessID)
popen file args minput =
    E.handle (\e -> return ([],show (e::E.IOException), error (show e))) $ do

    (inp,out,err,pid) <- runInteractiveProcess file args Nothing Nothing

    case minput of
        Just input -> hPutStr inp input >> hClose inp -- importante!
        Nothing    -> return ()

    -- Now, grab the input
    output <- hGetContents out
    errput <- hGetContents err

    -- SimonM sez:
    -- ... avoids blocking the main thread, but ensures that all the
    -- data gets pulled as it becomes available. you have to force the
    -- output strings before waiting for the process to terminate.
    --
    _ <- forkIO (E.evaluate (length output) >> return ())
    _ <- forkIO (E.evaluate (length errput) >> return ())

    -- And now we wait. We must wait after we read, unsurprisingly.
    exitCode <- waitForProcess pid -- blocks without -threaded, you're warned.
    case exitCode of
      ExitFailure code
          | null errput -> let errMsg = file ++ ": failed with error code " ++ show code
                           in return ([],errMsg,error errMsg)
      _ -> return (output,errput,pid)

#else

--
-- catch so that we can deal with forkProcess failing gracefully.  and
-- getProcessStatus is needed so as not to get a bunch of zombies,
-- leading to forkProcess failing.
--
-- Large amounts of input will cause problems with blocking as we wait
-- on the process to finish. Make sure no lambdabot processes will
-- generate 1000s of lines of output.
--
popen :: FilePath -> [String] -> Maybe String -> IO (String,String,P.ProcessID)
popen f s m =
        E.handle (\e -> return ([], show (e::IOException), error $ show e )) $ do
            x@(_,_,pid) <- P.popen f s m
            b <- P.getProcessStatus True False pid  -- wait
            return $ case b of
                Nothing -> ([], "process has disappeared", pid)
                _       -> x

#endif