module Hakyll.Core.UnixFilter
( unixFilter
, unixFilterLBS
) where
import Control.Concurrent (forkIO)
import System.Posix.Process (executeFile, forkProcess)
import System.Posix.IO ( dupTo, createPipe, stdInput
, stdOutput, closeFd, fdToHandle
)
import System.IO ( Handle, hPutStr, hClose, hGetContents
, hSetEncoding, localeEncoding
)
import Data.ByteString.Lazy (ByteString)
import qualified Data.ByteString.Lazy as LB
import Hakyll.Core.Compiler
unixFilter :: String
-> [String]
-> Compiler String String
unixFilter = unixFilterWith writer reader
where
writer handle input = do
hSetEncoding handle localeEncoding
hPutStr handle input
reader handle = do
hSetEncoding handle localeEncoding
hGetContents handle
unixFilterLBS :: String
-> [String]
-> Compiler ByteString ByteString
unixFilterLBS = unixFilterWith LB.hPutStr LB.hGetContents
unixFilterWith :: (Handle -> i -> IO ())
-> (Handle -> IO o)
-> String
-> [String]
-> Compiler i o
unixFilterWith writer reader programName args =
timedCompiler ("Executing external program " ++ programName) $
unsafeCompiler $ unixFilterIO writer reader programName args
unixFilterIO :: (Handle -> i -> IO ())
-> (Handle -> IO o)
-> String
-> [String]
-> i
-> IO o
unixFilterIO writer reader programName args input = do
(stdinRead, stdinWrite) <- createPipe
(stdoutRead, stdoutWrite) <- createPipe
_ <- forkProcess $ do
_ <- dupTo stdinRead stdInput
_ <- dupTo stdoutWrite stdOutput
mapM_ closeFd [stdinWrite, stdoutRead, stdinRead, stdoutWrite]
_ <- executeFile programName True args Nothing
return ()
mapM_ closeFd [stdinRead, stdoutWrite]
_ <- forkIO $ do
stdinWriteHandle <- fdToHandle stdinWrite
writer stdinWriteHandle input
hClose stdinWriteHandle
stdoutReadHandle <- fdToHandle stdoutRead
reader stdoutReadHandle