-----------------------------------------------------------------------------
-- |
-- Module      :  Distribution.Client.Compat.Process
-- Copyright   :  (c) 2013 Liu Hao, Brent Yorgey
-- License     :  BSD-style (see the file LICENSE)
--
-- Maintainer  :  cabal-devel@haskell.org
-- Stability   :  provisional
-- Portability :  portable
--
-- Cross-platform utilities for invoking processes.
--
-----------------------------------------------------------------------------

module Distribution.Client.Compat.Process (
  readProcessWithExitCode
) where

import Prelude (FilePath, IO, String, return, (||))

import           Control.Exception (catch, throw)
import           System.Exit       (ExitCode (ExitFailure))
import           System.IO.Error   (isDoesNotExistError, isPermissionError)
import qualified System.Process    as P

-- | @readProcessWithExitCode@ creates an external process, reads its
--   standard output and standard error strictly, waits until the
--   process terminates, and then returns the @ExitCode@ of the
--   process, the standard output, and the standard error.
--
--   See the documentation of the version from @System.Process@ for
--   more information.
--
--   The version from @System.Process@ behaves inconsistently across
--   platforms when an executable with the given name is not found: in
--   some cases it returns an @ExitFailure@, in others it throws an
--   exception.  This variant catches \"does not exist\" and
--   \"permission denied\" exceptions and turns them into
--   @ExitFailure@s.
--
-- TODO: this doesn't use 'Distrubution.Compat.Process'.
--
readProcessWithExitCode :: FilePath -> [String] -> String -> IO (ExitCode, String, String)
readProcessWithExitCode :: FilePath
-> [FilePath] -> FilePath -> IO (ExitCode, FilePath, FilePath)
readProcessWithExitCode FilePath
cmd [FilePath]
args FilePath
input =
  FilePath
-> [FilePath] -> FilePath -> IO (ExitCode, FilePath, FilePath)
P.readProcessWithExitCode FilePath
cmd [FilePath]
args FilePath
input
    IO (ExitCode, FilePath, FilePath)
-> (IOError -> IO (ExitCode, FilePath, FilePath))
-> IO (ExitCode, FilePath, FilePath)
forall e a. Exception e => IO a -> (e -> IO a) -> IO a
`catch` \IOError
e -> if IOError -> Bool
isDoesNotExistError IOError
e Bool -> Bool -> Bool
|| IOError -> Bool
isPermissionError IOError
e
                    then (ExitCode, FilePath, FilePath) -> IO (ExitCode, FilePath, FilePath)
forall (m :: * -> *) a. Monad m => a -> m a
return (Int -> ExitCode
ExitFailure Int
127, FilePath
"", FilePath
"")
                    else IOError -> IO (ExitCode, FilePath, FilePath)
forall a e. Exception e => e -> a
throw IOError
e