{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE ForeignFunctionInterface #-}
module System.Posix.Redirect
( redirectStdout
, redirectStderr
, redirectWriteHandle
, unsafeRedirectWriteFd
) where
import Control.Concurrent
import Control.Exception
import Control.Monad
import Data.ByteString as BS
import Foreign.C.Types
import Foreign.Ptr
import System.IO
import System.Posix.IO
import System.Posix.Types
unsafeRedirectWriteFd :: Fd -> IO a -> IO (ByteString, a)
unsafeRedirectWriteFd :: forall a. Fd -> IO a -> IO (ByteString, a)
unsafeRedirectWriteFd Fd
fd IO a
f = IO (Handle, (Fd, Fd))
-> ((Handle, (Fd, Fd)) -> IO ())
-> ((Handle, (Fd, Fd)) -> IO (ByteString, a))
-> IO (ByteString, a)
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
bracket IO (Handle, (Fd, Fd))
setup (Handle -> IO ()
hClose (Handle -> IO ())
-> ((Handle, (Fd, Fd)) -> Handle) -> (Handle, (Fd, Fd)) -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Handle, (Fd, Fd)) -> Handle
forall a b. (a, b) -> a
fst) (((Handle, (Fd, Fd)) -> IO (ByteString, a)) -> IO (ByteString, a))
-> ((Handle, (Fd, Fd)) -> IO (ByteString, a)) -> IO (ByteString, a)
forall a b. (a -> b) -> a -> b
$
\ (Handle
outHandle, (Fd
wfd, Fd
old)) -> do
outMVar <- IO (MVar ByteString)
forall a. IO (MVar a)
newEmptyMVar
void $ forkIO (BS.hGetContents outHandle >>= putMVar outMVar)
r <- f `finally` do
void $ dupTo old fd
closeFd wfd
out <- takeMVar outMVar
return (out, r)
where
setup :: IO (Handle, (Fd, Fd))
setup = do
(rfd, wfd) <- IO (Fd, Fd)
createPipe
old <- dup fd
void $ dupTo wfd fd
outHandle <- fdToHandle rfd
return (outHandle, (wfd, old))
redirectWriteHandle :: Fd -> Handle -> Ptr FILE -> IO a -> IO (ByteString, a)
redirectWriteHandle :: forall a. Fd -> Handle -> Ptr FILE -> IO a -> IO (ByteString, a)
redirectWriteHandle Fd
oldFd Handle
oldHandle Ptr FILE
cOldHandle IO a
f = do
Handle -> IO ()
hFlush Handle
oldHandle
Handle -> IO ()
hFlush Handle
stdout
IO CInt -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO CInt -> IO ()) -> IO CInt -> IO ()
forall a b. (a -> b) -> a -> b
$ Ptr FILE -> IO CInt
c_fflush Ptr FILE
cOldHandle
Fd -> IO a -> IO (ByteString, a)
forall a. Fd -> IO a -> IO (ByteString, a)
unsafeRedirectWriteFd Fd
oldFd (IO a -> IO (ByteString, a)) -> IO a -> IO (ByteString, a)
forall a b. (a -> b) -> a -> b
$ do
r <- IO a
f
hFlush oldHandle
void $ c_fflush cOldHandle
return r
redirectStdout :: IO a -> IO (ByteString, a)
redirectStdout :: forall a. IO a -> IO (ByteString, a)
redirectStdout IO a
f = do
c_stdout <- IO (Ptr FILE)
cio_stdout
redirectWriteHandle stdOutput stdout c_stdout f
redirectStderr :: IO a -> IO (ByteString, a)
redirectStderr :: forall a. IO a -> IO (ByteString, a)
redirectStderr IO a
f = do
c_stderr <- IO (Ptr FILE)
cio_stderr
redirectWriteHandle stdError stderr c_stderr f
data FILE
foreign import ccall safe "stdio.h fflush"
c_fflush :: Ptr FILE -> IO CInt
foreign import ccall unsafe "hsredirect.h PosixRedirect_stdout"
cio_stdout :: IO (Ptr FILE)
foreign import ccall unsafe "hsredirect.h PosixRedirect_stderr"
cio_stderr :: IO (Ptr FILE)