{-# LANGUAGE NoImplicitPrelude #-} -- | Run external pagers (@$PAGER@, @less@, @more@). module System.Process.Pager ( pageWriter , pageText , PagerException (..) ) where import Control.Monad.Trans.Maybe ( MaybeT (runMaybeT, MaybeT) ) import qualified Data.Text.IO as T import Stack.Prelude import System.Directory ( findExecutable ) import System.Environment ( lookupEnv ) import System.Process ( createProcess, cmdspec, shell, proc, waitForProcess , CmdSpec (ShellCommand, RawCommand) , StdStream (CreatePipe) , CreateProcess (std_in, close_fds, delegate_ctlc) ) -- | Type representing exceptions thrown by functions exported by the -- "System.Process.Pager" module. data PagerException = PagerExitFailure CmdSpec Int deriving (Show, Typeable) instance Exception PagerException where displayException (PagerExitFailure cmd n) = let getStr (ShellCommand c) = c getStr (RawCommand exePath _) = exePath in concat [ "Error: [S-9392]\n" , "Pager (`" , getStr cmd , "') exited with non-zero status: " , show n ] -- | Run pager, providing a function that writes to the pager's input. pageWriter :: (Handle -> IO ()) -> IO () pageWriter writer = do mpager <- runMaybeT $ cmdspecFromEnvVar <|> cmdspecFromExeName "less" <|> cmdspecFromExeName "more" case mpager of Just pager -> do (Just h,_,_,procHandle) <- createProcess pager { std_in = CreatePipe , close_fds = True , delegate_ctlc = True } (_ :: Either IOException ()) <- try (do writer h hClose h) exit <- waitForProcess procHandle case exit of ExitSuccess -> pure () ExitFailure n -> throwIO (PagerExitFailure (cmdspec pager) n) pure () Nothing -> writer stdout where cmdspecFromEnvVar = shell <$> MaybeT (lookupEnv "PAGER") cmdspecFromExeName = fmap (\command -> proc command []) . MaybeT . findExecutable -- | Run pager to display a 'Text' pageText :: Text -> IO () pageText = pageWriter . flip T.hPutStr