---------------------------------------------------------------------------- -- | -- Module : System.Texrunner -- Copyright : (c) 2014 Christopher Chalmers -- License : BSD-style (see LICENSE) -- Maintainer : c.chalmers@me.com -- -- Functions for running Tex. -- ----------------------------------------------------------------------------- module System.Texrunner ( runTex , runTex' , prettyPrintLog ) where import Control.Applicative import qualified Data.ByteString.Char8 as C8 hiding (concatMap) import Data.ByteString.Lazy.Char8 as LC8 hiding (concatMap) import Data.Maybe import System.Directory import System.Environment import System.Exit import System.FilePath import System.IO import System.IO.Temp import System.Process import System.Texrunner.Parse -- | Same as 'runTex'' but runs Tex in a temporary system directory. runTex :: String -- ^ Tex command -> [String] -- ^ Additional arguments -> [FilePath] -- ^ Additional Tex input paths -> ByteString -- ^ Source Tex file -> IO (ExitCode, TexLog, Maybe ByteString) runTex command args extras source = withSystemTempDirectory "texrunner." $ \path -> runTex' path command args extras source -- | Run Tex program in the given directory. Additional Tex inputs are -- for filepaths to things like images that Tex can refer to. runTex' :: FilePath -- ^ Directory to run Tex in -> String -- ^ Tex command -> [String] -- ^ Additional arguments -> [FilePath] -- ^ Additional Tex inputs -> ByteString -- ^ Source Tex file -> IO (ExitCode, TexLog, Maybe ByteString) runTex' path command args extras source = do LC8.writeFile (path "texrunner.tex") source environment <- extraTexInputs (path:extras) <$> getEnvironment let p = (proc command ("texrunner.tex" : args)) { cwd = Just path , std_in = CreatePipe , std_out = CreatePipe , env = Just environment } (Just inH, Just outH, _, pHandle) <- createProcess p hClose inH a <- C8.hGetContents outH -- backup log hClose outH exitC <- waitForProcess pHandle pdfExists <- doesFileExist (path "texrunner.pdf") pdfFile <- if pdfExists then Just <$> LC8.readFile (path "texrunner.pdf") else return Nothing logExists <- doesFileExist (path "texrunner.log") logFile <- if logExists then Just <$> C8.readFile (path "texrunner.log") else return Nothing -- pdfFile <- optional $ LC8.readFile (path "texrunner.pdf") -- logFile <- optional $ C8.readFile (path "texrunner.log") return (exitC, parseLog $ fromMaybe a logFile, pdfFile) -- | Add a list of paths to the tex extraTexInputs :: [FilePath] -> [(String,String)] -> [(String,String)] extraTexInputs [] = id extraTexInputs inputss = alter f "TEXINPUTS" where f Nothing = Just inputs f (Just x) = Just (inputs ++ [searchPathSeparator] ++ x) -- inputs = concatMap (++ [searchPathSeparator]) inputss -- inputs = intercalate [searchPathSeparator] inputss -- Alter can be used to insert, delete or update an element. Similar to alter -- in Data.Map. alter :: Eq k => (Maybe a -> Maybe a) -> k -> [(k,a)] -> [(k,a)] alter f k = go where go [] = maybeToList ((,) k <$> f Nothing) go ((k',x):xs) | k' == k = case f (Just x) of Just x' -> (k',x') : xs Nothing -> xs | otherwise = (k',x) : go xs