summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn MacFarlane <jgm@berkeley.edu>2017-04-02 23:02:55 +0200
committerJohn MacFarlane <jgm@berkeley.edu>2017-04-02 23:04:48 +0200
commit913db947a9cb43b6f449db2cd4c85fd74aa1ac8f (patch)
tree48af245903a81e6d80f1e8b7f4c8f519d1e7571b
parent9e78a9d26b73fa603025789a942f70306aaaad22 (diff)
Text.Pandoc.App: Throw errors rather than exiting.
These are caught (and lead to exit) in pandoc.hs, but other uses of Text.Pandoc.App may want to recover in another way. Added PandocAppError to PandocError (API change). This is a stopgap: later we should have a separate constructor for each type of error. Also fixed uses of 'exit' in Shared.readDataFile, and removed 'err' from Shared (API change). Finally, removed the dependency on extensible-exceptions. See #3548.
-rw-r--r--pandoc.cabal1
-rw-r--r--pandoc.hs5
-rw-r--r--src/Text/Pandoc/App.hs94
-rw-r--r--src/Text/Pandoc/Error.hs11
-rw-r--r--src/Text/Pandoc/Shared.hs16
-rw-r--r--src/Text/Pandoc/Templates.hs2
6 files changed, 72 insertions, 57 deletions
diff --git a/pandoc.cabal b/pandoc.cabal
index 16997486a..1588e532f 100644
--- a/pandoc.cabal
+++ b/pandoc.cabal
@@ -296,7 +296,6 @@ Library
texmath >= 0.9.4 && < 0.10,
xml >= 1.3.12 && < 1.4,
random >= 1 && < 1.2,
- extensible-exceptions >= 0.1 && < 0.2,
pandoc-types >= 1.17 && < 1.18,
aeson >= 0.7 && < 1.2,
aeson-pretty >= 0.8 && < 0.9,
diff --git a/pandoc.hs b/pandoc.hs
index f4fcd328a..6135aec03 100644
--- a/pandoc.hs
+++ b/pandoc.hs
@@ -34,6 +34,9 @@ writers.
-}
module Main where
import Text.Pandoc.App (convertWithOpts, defaultOpts, options, parseOptions)
+import Text.Pandoc.Error (handleError, PandocError)
+import qualified Control.Exception as E
main :: IO ()
-main = parseOptions options defaultOpts >>= convertWithOpts
+main = E.catch (parseOptions options defaultOpts >>= convertWithOpts)
+ (\(e :: PandocError) -> handleError (Left e))
diff --git a/src/Text/Pandoc/App.hs b/src/Text/Pandoc/App.hs
index 8f0410f12..b7ac4fd75 100644
--- a/src/Text/Pandoc/App.hs
+++ b/src/Text/Pandoc/App.hs
@@ -39,7 +39,6 @@ module Text.Pandoc.App (
) where
import Control.Applicative ((<|>))
import qualified Control.Exception as E
-import Control.Exception.Extensible (throwIO)
import Control.Monad
import Control.Monad.Trans
import Data.Aeson (eitherDecode', encode)
@@ -68,6 +67,7 @@ import System.FilePath
import System.IO (stderr, stdout)
import System.IO.Error (isDoesNotExistError)
import Text.Pandoc
+import Text.Pandoc.Error (PandocError(..))
import Text.Pandoc.Builder (setMeta)
import Text.Pandoc.Class (PandocIO, getLog, withMediaBag)
import Text.Pandoc.Highlighting (highlightingStyles)
@@ -76,7 +76,7 @@ import Text.Pandoc.MediaBag (MediaBag, extractMediaBag, mediaDirectory)
import Text.Pandoc.PDF (makePDF)
import Text.Pandoc.Process (pipeProcess)
import Text.Pandoc.SelfContained (makeSelfContained, makeDataURI)
-import Text.Pandoc.Shared (err, headerShift, openURL, readDataFile,
+import Text.Pandoc.Shared (headerShift, openURL, readDataFile,
readDataFileUTF8, safeRead, tabFilter)
import qualified Text.Pandoc.UTF8 as UTF8
import Text.Pandoc.Walk (walk)
@@ -98,7 +98,8 @@ parseOptions options' defaults = do
let unknownOptionErrors = foldr handleUnrecognizedOption [] unrecognizedOpts
unless (null errors && null unknownOptionErrors) $
- err 2 $ concat errors ++ unlines unknownOptionErrors ++
+ E.throwIO $ PandocAppError 2 $
+ concat errors ++ unlines unknownOptionErrors ++
("Try " ++ prg ++ " --help for more information.")
-- thread option data structure through all supplied option actions
@@ -175,7 +176,7 @@ convertWithOpts opts = do
(\o d -> liftIO $ writeCustom writerName o d)
:: Writer PandocIO)
else case getWriter writerName of
- Left e -> err 9 $
+ Left e -> E.throwIO $ PandocAppError 9 $
if format == "pdf"
then e ++
"\nTo create a pdf with pandoc, use " ++
@@ -189,7 +190,7 @@ convertWithOpts opts = do
-- the sake of the text2tags reader.
reader <- case getReader readerName of
Right r -> return (r :: Reader PandocIO)
- Left e -> err 7 e'
+ Left e -> E.throwIO $ PandocAppError 7 e'
where e' = case readerName of
"pdf" -> e ++
"\nPandoc can convert to PDF, but not from PDF."
@@ -204,7 +205,7 @@ convertWithOpts opts = do
Nothing -> do
deftemp <- getDefaultTemplate datadir format
case deftemp of
- Left e -> throwIO e
+ Left e -> E.throwIO e
Right t -> return (Just t)
Just tp -> do
-- strip off extensions
@@ -217,8 +218,8 @@ convertWithOpts opts = do
(readDataFileUTF8 datadir
("templates" </> tp'))
(\e' -> let _ = (e' :: E.SomeException)
- in throwIO e')
- else throwIO e)
+ in E.throwIO e')
+ else E.throwIO e)
let addStringAsVariable varname s vars = return $ (varname, s) : vars
@@ -304,7 +305,7 @@ convertWithOpts opts = do
let addSyntaxMap existingmap f = do
res <- parseSyntaxDefinition f
case res of
- Left errstr -> err 67 errstr
+ Left errstr -> E.throwIO $ PandocAppError 67 errstr
Right syn -> return $ addSyntaxDefinition syn existingmap
syntaxMap <- foldM addSyntaxMap defaultSyntaxMap
@@ -312,7 +313,8 @@ convertWithOpts opts = do
case missingIncludes (M.elems syntaxMap) of
[] -> return ()
- xs -> err 73 $ "Missing syntax definitions:\n" ++
+ xs -> E.throwIO $ PandocAppError 73 $
+ "Missing syntax definitions:\n" ++
unlines (map
(\(syn,dep) -> (T.unpack syn ++ " requires " ++
T.unpack dep ++ " through IncludeRules.")) xs)
@@ -358,7 +360,8 @@ convertWithOpts opts = do
istty <- queryTerminal stdOutput
#endif
when (istty && not (isTextFormat format) && outputFile == "-") $
- err 5 $ "Cannot write " ++ format ++ " output to stdout.\n" ++
+ E.throwIO $ PandocAppError 5 $
+ "Cannot write " ++ format ++ " output to stdout.\n" ++
"Specify an output file using the -o option."
@@ -386,7 +389,8 @@ convertWithOpts opts = do
Just logfile -> B.writeFile logfile (encodeLogMessages reports)
let isWarning msg = messageVerbosity msg == WARNING
when (optFailIfWarnings opts && any isWarning reports) $
- err 3 "Failing because there were warnings."
+ E.throwIO $
+ PandocAppError 3 "Failing because there were warnings."
return res
let sourceToDoc :: [FilePath] -> PandocIO (Pandoc, MediaBag)
@@ -429,8 +433,8 @@ convertWithOpts opts = do
-- make sure writer is latex, beamer, context, html5 or ms
unless (laTeXOutput || conTeXtOutput || html5Output ||
msOutput) $
- err 47 $ "cannot produce pdf output with " ++ format ++
- " writer"
+ liftIO $ E.throwIO $ PandocAppError 47 $
+ "cannot produce pdf output with " ++ format ++ " writer"
let pdfprog = case () of
_ | conTeXtOutput -> "context"
@@ -441,7 +445,8 @@ convertWithOpts opts = do
-- check for pdf creating program
mbPdfProg <- liftIO $ findExecutable pdfprog
when (isNothing mbPdfProg) $
- err 41 $ pdfprog ++ " not found. " ++
+ liftIO $ E.throwIO $ PandocAppError 41 $
+ pdfprog ++ " not found. " ++
pdfprog ++ " is needed for pdf output."
res <- makePDF pdfprog f writerOptions verbosity media doc'
@@ -450,7 +455,7 @@ convertWithOpts opts = do
Left err' -> liftIO $ do
B.hPutStr stderr err'
B.hPut stderr $ B.pack [10]
- err 43 "Error producing PDF"
+ E.throwIO $ PandocAppError 43 "Error producing PDF"
| otherwise -> do
let htmlFormat = format `elem`
["html","html4","html5","s5","slidy","slideous","dzslides","revealjs"]
@@ -492,19 +497,21 @@ externalFilter f args' d = liftIO $ do
unless (exists && isExecutable) $ do
mbExe <- findExecutable f'
when (isNothing mbExe) $
- err 83 $ "Error running filter " ++ f ++ ":\n" ++
- "Could not find executable '" ++ f' ++ "'."
+ E.throwIO $ PandocAppError 83 $
+ "Error running filter " ++ f ++ ":\n" ++
+ "Could not find executable '" ++ f' ++ "'."
env <- getEnvironment
let env' = Just $ ("PANDOC_VERSION", pandocVersion) : env
(exitcode, outbs) <- E.handle filterException $
pipeProcess env' f' args'' $ encode d
case exitcode of
ExitSuccess -> return $ either error id $ eitherDecode' outbs
- ExitFailure ec -> err 83 $ "Error running filter " ++ f ++ "\n" ++
- "Filter returned error status " ++ show ec
+ ExitFailure ec -> E.throwIO $ PandocAppError 83 $
+ "Error running filter " ++ f ++ "\n" ++
+ "Filter returned error status " ++ show ec
where filterException :: E.SomeException -> IO a
- filterException e = err 83 $ "Error running filter " ++ f ++ "\n" ++
- show e
+ filterException e = E.throwIO $ PandocAppError 83 $
+ "Error running filter " ++ f ++ "\n" ++ show e
-- | Data structure for command line options.
data Opt = Opt
@@ -806,12 +813,14 @@ lookupHighlightStyle (Just s)
| takeExtension s == ".theme" = -- attempt to load KDE theme
do contents <- B.readFile s
case parseTheme contents of
- Left _ -> err 69 $ "Could not read highlighting theme " ++ s
+ Left _ -> E.throwIO $ PandocAppError 69 $
+ "Could not read highlighting theme " ++ s
Right sty -> return (Just sty)
| otherwise =
case lookup (map toLower s) highlightingStyles of
Just sty -> return (Just sty)
- Nothing -> err 68 $ "Unknown highlight-style " ++ s
+ Nothing -> E.throwIO $ PandocAppError 68 $
+ "Unknown highlight-style " ++ s
-- | A list of functions, each transforming the options data structure
-- in response to a command-line option.
@@ -847,8 +856,8 @@ options =
case safeRead arg of
Just t | t > 0 && t < 6 ->
return opt{ optBaseHeaderLevel = t }
- _ -> err 19
- "base-header-level must be 1-5")
+ _ -> E.throwIO $ PandocAppError 19
+ "base-header-level must be 1-5")
"NUMBER")
"" -- "Headers base level"
@@ -881,8 +890,8 @@ options =
(\arg opt ->
case safeRead arg of
Just t | t > 0 -> return opt { optTabStop = t }
- _ -> err 31
- "tab-stop must be a number greater than 0")
+ _ -> E.throwIO $ PandocAppError 31
+ "tab-stop must be a number greater than 0")
"NUMBER")
"" -- "Tab stop (default 4)"
@@ -893,7 +902,7 @@ options =
"accept" -> return AcceptChanges
"reject" -> return RejectChanges
"all" -> return AllChanges
- _ -> err 6
+ _ -> E.throwIO $ PandocAppError 6
("Unknown option for track-changes: " ++ arg)
return opt { optTrackChanges = action })
"accept|reject|all")
@@ -964,7 +973,7 @@ options =
(\arg opt ->
case safeRead arg of
Just t | t > 0 -> return opt { optDpi = t }
- _ -> err 31
+ _ -> E.throwIO $ PandocAppError 31
"dpi must be a number greater than 0")
"NUMBER")
"" -- "Dpi (default 96)"
@@ -974,7 +983,8 @@ options =
(\arg opt ->
case safeRead ("Wrap" ++ uppercaseFirstLetter arg) of
Just o -> return opt { optWrapText = o }
- Nothing -> err 77 "--wrap must be auto, none, or preserve")
+ Nothing -> E.throwIO $ PandocAppError 77
+ "--wrap must be auto, none, or preserve")
"auto|none|preserve")
"" -- "Option for wrapping text in output"
@@ -983,7 +993,7 @@ options =
(\arg opt ->
case safeRead arg of
Just t | t > 0 -> return opt { optColumns = t }
- _ -> err 33
+ _ -> E.throwIO $ PandocAppError 33
"columns must be a number greater than 0")
"NUMBER")
"" -- "Length of line in characters"
@@ -999,7 +1009,7 @@ options =
case safeRead arg of
Just t | t >= 1 && t <= 6 ->
return opt { optTOCDepth = t }
- _ -> err 57
+ _ -> E.throwIO $ PandocAppError 57
"TOC level must be a number between 1 and 6")
"NUMBER")
"" -- "Number of levels to include in TOC"
@@ -1075,7 +1085,7 @@ options =
"block" -> return EndOfBlock
"section" -> return EndOfSection
"document" -> return EndOfDocument
- _ -> err 6
+ _ -> E.throwIO $ PandocAppError 6
("Unknown option for reference-location: " ++ arg)
return opt { optReferenceLocation = action })
"block|section|document")
@@ -1092,8 +1102,9 @@ options =
let tldName = "TopLevel" ++ uppercaseFirstLetter arg
case safeRead tldName of
Just tlDiv -> return opt { optTopLevelDivision = tlDiv }
- _ -> err 76 ("Top-level division must be " ++
- "section, chapter, part, or default"))
+ _ -> E.throwIO $ PandocAppError 76
+ ("Top-level division must be " ++
+ "section, chapter, part, or default"))
"section|chapter|part")
"" -- "Use top-level division type in LaTeX, ConTeXt, DocBook"
@@ -1108,7 +1119,8 @@ options =
case safeRead ('[':arg ++ "]") of
Just ns -> return opt { optNumberOffset = ns,
optNumberSections = True }
- _ -> err 57 "could not parse number-offset")
+ _ -> E.throwIO $ PandocAppError 57
+ "could not parse number-offset")
"NUMBERS")
"" -- "Starting number for sections, subsections, etc."
@@ -1128,7 +1140,7 @@ options =
case safeRead arg of
Just t | t >= 1 && t <= 6 ->
return opt { optSlideLevel = Just t }
- _ -> err 39
+ _ -> E.throwIO $ PandocAppError 39
"slide level must be a number between 1 and 6")
"NUMBER")
"" -- "Force header level for slides"
@@ -1151,7 +1163,7 @@ options =
"references" -> return ReferenceObfuscation
"javascript" -> return JavascriptObfuscation
"none" -> return NoObfuscation
- _ -> err 6
+ _ -> E.throwIO $ PandocAppError 6
("Unknown obfuscation method: " ++ arg)
return opt { optEmailObfuscation = method })
"none|javascript|references")
@@ -1213,7 +1225,7 @@ options =
case safeRead arg of
Just t | t >= 1 && t <= 6 ->
return opt { optEpubChapterLevel = t }
- _ -> err 59
+ _ -> E.throwIO $ PandocAppError 59
"chapter level must be a number between 1 and 6")
"NUMBER")
"" -- "Header level at which to split chapters in EPUB"
@@ -1224,7 +1236,7 @@ options =
let b = takeBaseName arg
if b `elem` ["pdflatex", "lualatex", "xelatex"]
then return opt { optLaTeXEngine = arg }
- else err 45 "latex-engine must be pdflatex, lualatex, or xelatex.")
+ else E.throwIO $ PandocAppError 45 "latex-engine must be pdflatex, lualatex, or xelatex.")
"PROGRAM")
"" -- "Name of latex program to use in generating PDF"
diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs
index 4b38348ac..252c469b1 100644
--- a/src/Text/Pandoc/Error.hs
+++ b/src/Text/Pandoc/Error.hs
@@ -37,9 +37,11 @@ module Text.Pandoc.Error (
import Control.Exception (Exception)
import Data.Generics (Typeable)
import GHC.Generics (Generic)
-import Text.Pandoc.Shared (err)
import Text.Parsec.Error
import Text.Parsec.Pos hiding (Line)
+import qualified Text.Pandoc.UTF8 as UTF8
+import System.Exit (exitWith, ExitCode(..))
+import System.IO (stderr)
type Input = String
@@ -49,6 +51,7 @@ data PandocError = PandocIOError String IOError
| PandocParseError String
| PandocParsecError Input ParseError
| PandocMakePDFError String
+ | PandocAppError Int String
deriving (Show, Typeable, Generic)
instance Exception PandocError
@@ -74,4 +77,10 @@ handleError (Left e) =
else ""
in err 65 $ "\nError at " ++ show err' ++ errorInFile
PandocMakePDFError s -> err 65 s
+ PandocAppError ec s -> err ec s
+err :: Int -> String -> IO a
+err exitCode msg = do
+ UTF8.hPutStrLn stderr msg
+ exitWith $ ExitFailure exitCode
+ return undefined
diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs
index 3b9ae7501..dfdbaf428 100644
--- a/src/Text/Pandoc/Shared.hs
+++ b/src/Text/Pandoc/Shared.hs
@@ -82,7 +82,6 @@ module Text.Pandoc.Shared (
collapseFilePath,
filteredFilesFromArchive,
-- * Error handling
- err,
mapLeft,
-- * for squashing blocks
blocksToInlines,
@@ -99,7 +98,6 @@ import Text.Pandoc.Walk
import Text.Pandoc.Builder (Inlines, Blocks, ToMetaValue(..))
import qualified Text.Pandoc.Builder as B
import qualified Text.Pandoc.UTF8 as UTF8
-import System.Exit (exitWith, ExitCode(..))
import Data.Char ( toLower, isLower, isUpper, isAlpha,
isLetter, isDigit, isSpace )
import Data.List ( find, stripPrefix, intercalate )
@@ -112,16 +110,15 @@ import System.Directory
import System.FilePath (splitDirectories, isPathSeparator)
import qualified System.FilePath.Posix as Posix
import Text.Pandoc.MIME (MimeType)
+import Text.Pandoc.Error (PandocError(..))
import System.FilePath ( (</>) )
import Data.Generics (Typeable, Data)
import qualified Control.Monad.State as S
-import Control.Monad.Trans (MonadIO (..))
import qualified Control.Exception as E
import Control.Monad (msum, unless, MonadPlus(..))
import Text.Pandoc.Pretty (charWidth)
import Text.Pandoc.Compat.Time
import Data.Time.Clock.POSIX
-import System.IO (stderr)
import System.IO.Error
import System.IO.Temp
import Text.HTML.TagSoup (renderTagsOptions, RenderOptions(..), Tag(..),
@@ -677,7 +674,8 @@ readDefaultDataFile "reference.odt" =
readDefaultDataFile fname =
#ifdef EMBED_DATA_FILES
case lookup (makeCanonical fname) dataFiles of
- Nothing -> err 97 $ "Could not find data file " ++ fname
+ Nothing -> E.throwIO $ PandocAppError 97 $
+ "Could not find data file " ++ fname
Just contents -> return contents
where makeCanonical = Posix.joinPath . transformPathParts . splitDirectories
transformPathParts = reverse . foldl go []
@@ -693,7 +691,7 @@ checkExistence fn = do
exists <- doesFileExist fn
if exists
then return fn
- else err 97 ("Could not find data file " ++ fn)
+ else E.throwIO $ PandocAppError 97 ("Could not find data file " ++ fn)
#endif
-- | Read file from specified user data directory or, if not found there, from
@@ -759,12 +757,6 @@ openURL u
-- Error reporting
--
-err :: MonadIO m => Int -> String -> m a
-err exitCode msg = liftIO $ do
- UTF8.hPutStrLn stderr msg
- exitWith $ ExitFailure exitCode
- return undefined
-
mapLeft :: (a -> b) -> Either a c -> Either b c
mapLeft f (Left x) = Left (f x)
mapLeft _ (Right x) = Right x
diff --git a/src/Text/Pandoc/Templates.hs b/src/Text/Pandoc/Templates.hs
index 4ae2e80d7..26aeb9a73 100644
--- a/src/Text/Pandoc/Templates.hs
+++ b/src/Text/Pandoc/Templates.hs
@@ -41,7 +41,7 @@ module Text.Pandoc.Templates ( renderTemplate
, Template
, getDefaultTemplate ) where
-import qualified Control.Exception.Extensible as E (IOException, try)
+import qualified Control.Exception as E (IOException, try)
import Data.Aeson (ToJSON (..))
import qualified Data.Text as T
import System.FilePath ((<.>), (</>))