3Copyright (C) 2006-2013 John MacFarlane <jgm@berkeley.edu>
5This program is free software; you can redistribute it and/or modify
6it under the terms of the GNU General Public License as published by
7the Free Software Foundation; either version 2 of the License, or
8(at your option) any later version.
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 Copyright : Copyright (C) 2006-2013 John MacFarlane
23 License : GNU GPL, version 2 or above
25 Maintainer : John MacFarlane <jgm@berkeley@edu>
27 Portability : portable
29Parses command-line options and calls the appropriate readers and
34import Text.Pandoc.Builder (setMeta)
35import Text.Pandoc.PDF (makePDF)
36import Text.Pandoc.Readers.LaTeX (handleIncludes)
37import Text.Pandoc.Shared ( tabFilter, readDataFileUTF8, readDataFile,
38 safeRead, headerShift, normalize, err, warn,
40import Text.Pandoc.XML ( toEntities )
41import Text.Pandoc.SelfContained ( makeSelfContained )
42import Text.Pandoc.Process (pipeProcess)
43import Text.Highlighting.Kate ( languages, Style, tango, pygments,
44 espresso, zenburn, kate, haddock, monochrome )
45import System.Environment ( getArgs, getProgName )
46import System.Exit ( exitWith, ExitCode (..) )
48import System.Console.GetOpt
49import Data.Char ( toLower )
50import Data.List ( intercalate, isPrefixOf, sort )
51import System.Directory ( getAppUserDataDirectory, findExecutable )
52import System.IO ( stdout, stderr )
53import System.IO.Error ( isDoesNotExistError )
54import qualified Control.Exception as E
55import Control.Exception.Extensible ( throwIO )
56import qualified Text.Pandoc.UTF8 as UTF8
57import Control.Monad (when, unless, liftM)
58import Data.Foldable (foldrM)
59import Network.URI (parseURI, isURI, URI(..))
60import qualified Data.ByteString.Lazy as B
61import qualified Data.ByteString as BS
62import Data.Aeson (eitherDecode', encode)
63import qualified Data.Map as M
64import System.IO.Error(ioeGetErrorType)
65import GHC.IO.Exception (IOErrorType(ResourceVanished))
66import Data.Yaml (decode)
67import qualified Data.Yaml as Yaml
68import qualified Data.Text as T
70copyrightMessage :: String
71copyrightMessage = "\nCopyright (C) 2006-2013 John MacFarlane\n" ++
72 "Web: http://johnmacfarlane.net/pandoc\n" ++
73 "This is free software; see the source for copying conditions. There is no\n" ++
74 "warranty, not even for merchantability or fitness for a particular purpose."
78 "\nCompiled with texmath " ++
79 VERSION_texmath ++ ", highlighting-kate " ++ VERSION_highlighting_kate ++
80 ".\nSyntax highlighting is supported for the following languages:\n " ++
82 [map toLower l | l <- languages, l /= "Alert" && l /= "Alert_indent"]
84-- | Converts a list of strings into a single string with the items printed as
85-- comma separated words in lines with a maximum line length.
86wrapWords :: Int -> Int -> [String] -> String
87wrapWords indent c = wrap' (c - indent) (c - indent)
88 where wrap' _ _ [] = ""
89 wrap' cols remaining (x:xs) = if remaining == cols
90 then x ++ wrap' cols (remaining - length x) xs
91 else if (length x + 1) > remaining
92 then ",\n" ++ replicate indent ' ' ++ x ++ wrap' cols (cols - length x) xs
93 else ", " ++ x ++ wrap' cols (remaining - (length x + 2)) xs
95isTextFormat :: String -> Bool
96isTextFormat s = takeWhile (`notElem` "+-") s `notElem` ["odt","docx","epub","epub3"]
98externalFilter :: FilePath -> [String] -> Pandoc -> IO Pandoc
99externalFilter f args' d = do
100 (exitcode, outbs, errbs) <- E.handle filterException $
101 pipeProcess Nothing f args' $ encode d
102 when (not $ B.null errbs) $ B.hPutStr stderr errbs
104 ExitSuccess -> return $ either error id $ eitherDecode' outbs
105 ExitFailure _ -> err 83 $ "Error running filter " ++ f
106 where filterException :: E.SomeException -> IO a
107 filterException e = err 83 $ "Error running filter " ++ f ++ "\n" ++
108 if ioeGetErrorType `fmap` E.fromException e ==
109 Just ResourceVanished
110 then f ++ " not found in path"
113-- | Data structure for command line options.
115 { optTabStop :: Int -- ^ Number of spaces per tab
116 , optPreserveTabs :: Bool -- ^ Preserve tabs instead of converting to spaces
117 , optStandalone :: Bool -- ^ Include header, footer
118 , optReader :: String -- ^ Reader format
119 , optWriter :: String -- ^ Writer format
120 , optParseRaw :: Bool -- ^ Parse unconvertable HTML and TeX
121 , optTableOfContents :: Bool -- ^ Include table of contents
122 , optTransforms :: [Pandoc -> Pandoc] -- ^ Doc transforms to apply
123 , optTemplate :: Maybe FilePath -- ^ Custom template
124 , optVariables :: [(String,String)] -- ^ Template variables to set
125 , optMetadata :: M.Map String MetaValue -- ^ Metadata fields to set
126 , optOutputFile :: String -- ^ Name of output file
127 , optNumberSections :: Bool -- ^ Number sections in LaTeX
128 , optNumberOffset :: [Int] -- ^ Starting number for sections
129 , optSectionDivs :: Bool -- ^ Put sections in div tags in HTML
130 , optIncremental :: Bool -- ^ Use incremental lists in Slidy/Slideous/S5
131 , optSelfContained :: Bool -- ^ Make HTML accessible offline
132 , optSmart :: Bool -- ^ Use smart typography
133 , optOldDashes :: Bool -- ^ Parse dashes like pandoc <=1.8.2.1
134 , optHtml5 :: Bool -- ^ Produce HTML5 in HTML
135 , optHtmlQTags :: Bool -- ^ Use <q> tags in HTML
136 , optHighlight :: Bool -- ^ Highlight source code
137 , optHighlightStyle :: Style -- ^ Style to use for highlighted code
138 , optChapters :: Bool -- ^ Use chapter for top-level sects
139 , optHTMLMathMethod :: HTMLMathMethod -- ^ Method to print HTML math
140 , optReferenceODT :: Maybe FilePath -- ^ Path of reference.odt
141 , optReferenceDocx :: Maybe FilePath -- ^ Path of reference.docx
142 , optEpubStylesheet :: Maybe String -- ^ EPUB stylesheet
143 , optEpubMetadata :: String -- ^ EPUB metadata
144 , optEpubFonts :: [FilePath] -- ^ EPUB fonts to embed
145 , optEpubChapterLevel :: Int -- ^ Header level at which to split chapters
146 , optTOCDepth :: Int -- ^ Number of levels to include in TOC
147 , optDumpArgs :: Bool -- ^ Output command-line arguments
148 , optIgnoreArgs :: Bool -- ^ Ignore command-line arguments
149 , optReferenceLinks :: Bool -- ^ Use reference links in writing markdown, rst
150 , optWrapText :: Bool -- ^ Wrap text
151 , optColumns :: Int -- ^ Line length in characters
152 , optFilters :: [FilePath] -- ^ Filters to apply
153 , optEmailObfuscation :: ObfuscationMethod
154 , optIdentifierPrefix :: String
155 , optIndentedCodeClasses :: [String] -- ^ Default classes for indented code blocks
156 , optDataDir :: Maybe FilePath
157 , optCiteMethod :: CiteMethod -- ^ Method to output cites
158 , optListings :: Bool -- ^ Use listings package for code blocks
159 , optLaTeXEngine :: String -- ^ Program to use for latex -> pdf
160 , optSlideLevel :: Maybe Int -- ^ Header level that creates slides
161 , optSetextHeaders :: Bool -- ^ Use atx headers for markdown level 1-2
162 , optAscii :: Bool -- ^ Use ascii characters only in html
163 , optTeXLigatures :: Bool -- ^ Use TeX ligatures for quotes/dashes
164 , optDefaultImageExtension :: String -- ^ Default image extension
167-- | Defaults for command-line options.
171 , optPreserveTabs = False
172 , optStandalone = False
173 , optReader = "" -- null for default reader
174 , optWriter = "" -- null for default writer
175 , optParseRaw = False
176 , optTableOfContents = False
178 , optTemplate = Nothing
180 , optMetadata = M.empty
181 , optOutputFile = "-" -- "-" means stdout
182 , optNumberSections = False
183 , optNumberOffset = [0,0,0,0,0,0]
184 , optSectionDivs = False
185 , optIncremental = False
186 , optSelfContained = False
188 , optOldDashes = False
190 , optHtmlQTags = False
191 , optHighlight = True
192 , optHighlightStyle = pygments
193 , optChapters = False
194 , optHTMLMathMethod = PlainMath
195 , optReferenceODT = Nothing
196 , optReferenceDocx = Nothing
197 , optEpubStylesheet = Nothing
198 , optEpubMetadata = ""
200 , optEpubChapterLevel = 1
202 , optDumpArgs = False
203 , optIgnoreArgs = False
204 , optReferenceLinks = False
208 , optEmailObfuscation = JavascriptObfuscation
209 , optIdentifierPrefix = ""
210 , optIndentedCodeClasses = []
211 , optDataDir = Nothing
212 , optCiteMethod = Citeproc
213 , optListings = False
214 , optLaTeXEngine = "pdflatex"
215 , optSlideLevel = Nothing
216 , optSetextHeaders = True
218 , optTeXLigatures = True
219 , optDefaultImageExtension = ""
222-- | A list of functions, each transforming the options data structure
223-- in response to a command-line option.
224options :: [OptDescr (Opt -> IO Opt)]
226 [ Option "fr" ["from","read"]
228 (\arg opt -> return opt { optReader = map toLower arg })
232 , Option "tw" ["to","write"]
234 (\arg opt -> return opt { optWriter = map toLower arg })
238 , Option "o" ["output"]
240 (\arg opt -> return opt { optOutputFile = arg })
242 "" -- "Name of output file"
244 , Option "" ["data-dir"]
246 (\arg opt -> return opt { optDataDir = Just arg })
247 "DIRECTORY") -- "Directory containing pandoc data files."
250 , Option "" ["strict"]
253 err 59 $ "The --strict option has been removed.\n" ++
254 "Use `markdown_strict' input or output format instead."
256 "" -- "Disable markdown syntax extensions"
258 , Option "R" ["parse-raw"]
260 (\opt -> return opt { optParseRaw = True }))
261 "" -- "Parse untranslatable HTML codes and LaTeX environments as raw"
263 , Option "S" ["smart"]
265 (\opt -> return opt { optSmart = True }))
266 "" -- "Use smart quotes, dashes, and ellipses"
268 , Option "" ["old-dashes"]
270 (\opt -> return opt { optSmart = True
271 , optOldDashes = True }))
272 "" -- "Use smart quotes, dashes, and ellipses"
274 , Option "" ["base-header-level"]
279 let oldTransforms = optTransforms opt
281 return opt{ optTransforms =
282 headerShift shift : oldTransforms }
284 "base-header-level must be a number > 0")
286 "" -- "Headers base level"
288 , Option "" ["indented-code-classes"]
290 (\arg opt -> return opt { optIndentedCodeClasses = words $
291 map (\c -> if c == ',' then ' ' else c) arg })
293 "" -- "Classes (whitespace- or comma-separated) to use for indented code-blocks"
295 , Option "F" ["filter"]
297 (\arg opt -> return opt { optFilters = arg : optFilters opt })
299 "" -- "External JSON filter"
301 , Option "" ["normalize"]
303 (\opt -> return opt { optTransforms =
304 normalize : optTransforms opt } ))
305 "" -- "Normalize the Pandoc AST"
307 , Option "p" ["preserve-tabs"]
309 (\opt -> return opt { optPreserveTabs = True }))
310 "" -- "Preserve tabs instead of converting to spaces"
312 , Option "" ["tab-stop"]
316 Just t | t > 0 -> return opt { optTabStop = t }
318 "tab-stop must be a number greater than 0")
320 "" -- "Tab stop (default 4)"
322 , Option "s" ["standalone"]
324 (\opt -> return opt { optStandalone = True }))
325 "" -- "Include needed header and footer on output"
327 , Option "" ["template"]
330 return opt{ optTemplate = Just arg,
331 optStandalone = True })
333 "" -- "Use custom template"
335 , Option "M" ["metadata"]
338 let (key,val) = case break (`elem` ":=") arg of
339 (k,_:v) -> (k, readMetaValue v)
340 (k,_) -> (k, MetaBool True)
341 return opt{ optMetadata = addMetadata key val
346 , Option "V" ["variable"]
349 let (key,val) = case break (`elem` ":=") arg of
352 return opt{ optVariables = (key,val) : optVariables opt })
356 , Option "D" ["print-default-template"]
359 templ <- getDefaultTemplate Nothing arg
361 Right t -> UTF8.hPutStr stdout t
362 Left e -> error $ show e
363 exitWith ExitSuccess)
365 "" -- "Print default template for FORMAT"
367 , Option "" ["print-default-data-file"]
370 readDataFile Nothing arg >>= BS.hPutStr stdout
371 exitWith ExitSuccess)
373 "" -- "Print default data file"
375 , Option "" ["no-wrap"]
377 (\opt -> return opt { optWrapText = False }))
378 "" -- "Do not wrap text in output"
380 , Option "" ["columns"]
384 Just t | t > 0 -> return opt { optColumns = t }
386 "columns must be a number greater than 0")
388 "" -- "Length of line in characters"
390 , Option "" ["toc", "table-of-contents"]
392 (\opt -> return opt { optTableOfContents = True }))
393 "" -- "Include table of contents"
395 , Option "" ["toc-depth"]
399 Just t | t >= 1 && t <= 6 ->
400 return opt { optTOCDepth = t }
402 "TOC level must be a number between 1 and 6")
404 "" -- "Number of levels to include in TOC"
406 , Option "" ["no-highlight"]
408 (\opt -> return opt { optHighlight = False }))
409 "" -- "Don't highlight source code"
411 , Option "" ["highlight-style"]
414 newStyle <- case map toLower arg of
415 "pygments" -> return pygments
416 "tango" -> return tango
417 "espresso" -> return espresso
418 "zenburn" -> return zenburn
419 "kate" -> return kate
420 "monochrome" -> return monochrome
421 "haddock" -> return haddock
423 "Unknown style :" ++ arg
424 return opt{ optHighlightStyle = newStyle })
426 "" -- "Style for highlighted code"
428 , Option "H" ["include-in-header"]
431 text <- UTF8.readFile arg
432 -- add new ones to end, so they're included in order specified
433 let newvars = optVariables opt ++ [("header-includes",text)]
434 return opt { optVariables = newvars,
435 optStandalone = True })
437 "" -- "File to include at end of header (implies -s)"
439 , Option "B" ["include-before-body"]
442 text <- UTF8.readFile arg
443 -- add new ones to end, so they're included in order specified
444 let newvars = optVariables opt ++ [("include-before",text)]
445 return opt { optVariables = newvars,
446 optStandalone = True })
448 "" -- "File to include before document body"
450 , Option "A" ["include-after-body"]
453 text <- UTF8.readFile arg
454 -- add new ones to end, so they're included in order specified
455 let newvars = optVariables opt ++ [("include-after",text)]
456 return opt { optVariables = newvars,
457 optStandalone = True })
459 "" -- "File to include after document body"
461 , Option "" ["self-contained"]
463 (\opt -> return opt { optSelfContained = True,
464 optStandalone = True }))
465 "" -- "Make slide shows include all the needed js and css"
467 , Option "" ["offline"]
469 (\opt -> do warn $ "--offline is deprecated. Use --self-contained instead."
470 return opt { optSelfContained = True,
471 optStandalone = True }))
472 "" -- "Make slide shows include all the needed js and css"
473 -- deprecated synonym for --self-contained
475 , Option "5" ["html5"]
478 warn $ "--html5 is deprecated. "
479 ++ "Use the html5 output format instead."
480 return opt { optHtml5 = True }))
481 "" -- "Produce HTML5 in HTML output"
483 , Option "" ["html-q-tags"]
486 return opt { optHtmlQTags = True }))
487 "" -- "Use <q> tags for quotes in HTML"
489 , Option "" ["ascii"]
491 (\opt -> return opt { optAscii = True }))
492 "" -- "Use ascii characters only in HTML output"
494 , Option "" ["reference-links"]
496 (\opt -> return opt { optReferenceLinks = True } ))
497 "" -- "Use reference links in parsing HTML"
499 , Option "" ["atx-headers"]
501 (\opt -> return opt { optSetextHeaders = False } ))
502 "" -- "Use atx-style headers for markdown"
504 , Option "" ["chapters"]
506 (\opt -> return opt { optChapters = True }))
507 "" -- "Use chapter for top-level sections in LaTeX, DocBook"
509 , Option "N" ["number-sections"]
511 (\opt -> return opt { optNumberSections = True }))
512 "" -- "Number sections in LaTeX"
514 , Option "" ["number-offset"]
517 case safeRead ('[':arg ++ "]") of
518 Just ns -> return opt { optNumberOffset = ns,
519 optNumberSections = True }
520 _ -> err 57 "could not parse number-offset")
522 "" -- "Starting number for sections, subsections, etc."
524 , Option "" ["no-tex-ligatures"]
526 (\opt -> return opt { optTeXLigatures = False }))
527 "" -- "Don't use tex ligatures for quotes, dashes"
529 , Option "" ["listings"]
531 (\opt -> return opt { optListings = True }))
532 "" -- "Use listings package for LaTeX code blocks"
534 , Option "i" ["incremental"]
536 (\opt -> return opt { optIncremental = True }))
537 "" -- "Make list items display incrementally in Slidy/Slideous/S5"
539 , Option "" ["slide-level"]
543 Just t | t >= 1 && t <= 6 ->
544 return opt { optSlideLevel = Just t }
546 "slide level must be a number between 1 and 6")
548 "" -- "Force header level for slides"
550 , Option "" ["section-divs"]
552 (\opt -> return opt { optSectionDivs = True }))
553 "" -- "Put sections in div tags in HTML"
555 , Option "" ["default-image-extension"]
557 (\arg opt -> return opt { optDefaultImageExtension = arg })
559 "" -- "Default extension for extensionless images"
561 , Option "" ["email-obfuscation"]
564 method <- case arg of
565 "references" -> return ReferenceObfuscation
566 "javascript" -> return JavascriptObfuscation
567 "none" -> return NoObfuscation
569 ("Unknown obfuscation method: " ++ arg)
570 return opt { optEmailObfuscation = method })
571 "none|javascript|references")
572 "" -- "Method for obfuscating email in HTML"
574 , Option "" ["id-prefix"]
576 (\arg opt -> return opt { optIdentifierPrefix = arg })
578 "" -- "Prefix to add to automatically generated HTML identifiers"
580 , Option "T" ["title-prefix"]
583 let newvars = ("title-prefix", arg) : optVariables opt
584 return opt { optVariables = newvars,
585 optStandalone = True })
587 "" -- "String to prefix to HTML window title"
592 -- add new link to end, so it is included in proper order
593 let newvars = optVariables opt ++ [("css",arg)]
594 return opt { optVariables = newvars,
595 optStandalone = True })
597 "" -- "Link to CSS style sheet"
599 , Option "" ["reference-odt"]
602 return opt { optReferenceODT = Just arg })
604 "" -- "Path of custom reference.odt"
606 , Option "" ["reference-docx"]
609 return opt { optReferenceDocx = Just arg })
611 "" -- "Path of custom reference.docx"
613 , Option "" ["epub-stylesheet"]
616 text <- UTF8.readFile arg
617 return opt { optEpubStylesheet = Just text })
619 "" -- "Path of epub.css"
621 , Option "" ["epub-cover-image"]
624 return opt { optVariables =
625 ("epub-cover-image", arg) : optVariables opt })
627 "" -- "Path of epub cover image"
629 , Option "" ["epub-metadata"]
632 text <- UTF8.readFile arg
633 return opt { optEpubMetadata = text })
635 "" -- "Path of epub metadata file"
637 , Option "" ["epub-embed-font"]
640 return opt{ optEpubFonts = arg : optEpubFonts opt })
642 "" -- "Directory of fonts to embed"
644 , Option "" ["epub-chapter-level"]
648 Just t | t >= 1 && t <= 6 ->
649 return opt { optEpubChapterLevel = t }
651 "chapter level must be a number between 1 and 6")
653 "" -- "Header level at which to split chapters in EPUB"
655 , Option "" ["latex-engine"]
658 let b = takeBaseName arg
659 if (b == "pdflatex" || b == "lualatex" || b == "xelatex")
660 then return opt { optLaTeXEngine = arg }
661 else err 45 "latex-engine must be pdflatex, lualatex, or xelatex.")
663 "" -- "Name of latex program to use in generating PDF"
665 , Option "" ["bibliography"]
667 (\arg opt -> return opt{ optMetadata = addMetadata
668 "bibliography" (readMetaValue arg)
677 return opt{ optMetadata = addMetadata "csl"
683 , Option "" ["citation-abbreviations"]
686 return opt{ optMetadata = addMetadata
687 "citation-abbreviations"
693 , Option "" ["natbib"]
695 (\opt -> return opt { optCiteMethod = Natbib }))
696 "" -- "Use natbib cite commands in LaTeX output"
698 , Option "" ["biblatex"]
700 (\opt -> return opt { optCiteMethod = Biblatex }))
701 "" -- "Use biblatex cite commands in LaTeX output"
703 , Option "m" ["latexmathml", "asciimathml"]
706 return opt { optHTMLMathMethod = LaTeXMathML arg })
708 "" -- "Use LaTeXMathML script in html output"
710 , Option "" ["mathml"]
713 return opt { optHTMLMathMethod = MathML arg })
715 "" -- "Use mathml for HTML math"
717 , Option "" ["mimetex"]
720 let url' = case arg of
722 Nothing -> "/cgi-bin/mimetex.cgi?"
723 return opt { optHTMLMathMethod = WebTeX url' })
725 "" -- "Use mimetex for HTML math"
727 , Option "" ["webtex"]
730 let url' = case arg of
732 Nothing -> "http://chart.apis.google.com/chart?cht=tx&chl="
733 return opt { optHTMLMathMethod = WebTeX url' })
735 "" -- "Use web service for HTML math"
737 , Option "" ["jsmath"]
739 (\arg opt -> return opt { optHTMLMathMethod = JsMath arg})
741 "" -- "Use jsMath for HTML math"
743 , Option "" ["mathjax"]
746 let url' = case arg of
748 Nothing -> "http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"
749 return opt { optHTMLMathMethod = MathJax url'})
751 "" -- "Use MathJax for HTML math"
753 , Option "" ["gladtex"]
755 (\opt -> return opt { optHTMLMathMethod = GladTeX }))
756 "" -- "Use gladtex for HTML math"
758 , Option "" ["dump-args"]
760 (\opt -> return opt { optDumpArgs = True }))
761 "" -- "Print output filename and arguments to stdout."
763 , Option "" ["ignore-args"]
765 (\opt -> return opt { optIgnoreArgs = True }))
766 "" -- "Ignore command-line arguments."
768 , Option "v" ["version"]
772 defaultDatadir <- getAppUserDataDirectory "pandoc"
773 UTF8.hPutStrLn stdout (prg ++ " " ++ pandocVersion ++
774 compileInfo ++ "\nDefault user data directory: " ++
775 defaultDatadir ++ copyrightMessage)
776 exitWith ExitSuccess ))
777 "" -- "Print version"
779 , Option "h" ["help"]
783 UTF8.hPutStr stdout (usageMessage prg options)
784 exitWith ExitSuccess ))
789addMetadata :: String -> MetaValue -> M.Map String MetaValue
790 -> M.Map String MetaValue
791addMetadata k v m = case M.lookup k m of
792 Nothing -> M.insert k v m
793 Just (MetaList xs) -> M.insert k
794 (MetaList (xs ++ [v])) m
795 Just x -> M.insert k (MetaList [v, x]) m
797readMetaValue :: String -> MetaValue
798readMetaValue s = case decode (UTF8.fromString s) of
799 Just (Yaml.String t) -> MetaString $ T.unpack t
800 Just (Yaml.Bool b) -> MetaBool b
803-- Returns usage message
804usageMessage :: String -> [OptDescr (Opt -> IO Opt)] -> String
805usageMessage programName = usageInfo
806 (programName ++ " [OPTIONS] [FILES]" ++ "\nInput formats: " ++
807 (wrapWords 16 78 $ readers'names) ++ "\nOutput formats: " ++
808 (wrapWords 16 78 $ writers'names) ++
809 '\n' : replicate 16 ' ' ++
810 "[*for pdf output, use latex or beamer and -o FILENAME.pdf]\nOptions:")
812 writers'names = sort $ "pdf*" : map fst writers
813 readers'names = sort $ map fst readers
815-- Determine default reader based on source file extensions
816defaultReaderName :: String -> [FilePath] -> String
817defaultReaderName fallback [] = fallback
818defaultReaderName fallback (x:xs) =
819 case takeExtension (map toLower x) of
827 ".lhs" -> "markdown+lhs"
830 ".wiki" -> "mediawiki"
831 ".textile" -> "textile"
832 ".native" -> "native"
834 _ -> defaultReaderName fallback xs
836-- Returns True if extension of first source is .lhs
837lhsExtension :: [FilePath] -> Bool
838lhsExtension (x:_) = takeExtension x == ".lhs"
839lhsExtension _ = False
841-- Determine default writer based on output file extension
842defaultWriterName :: FilePath -> String
843defaultWriterName "-" = "html" -- no output file
845 case takeExtension (map toLower x) of
846 "" -> "markdown" -- empty extension
850 ".context" -> "context"
855 ".native" -> "native"
858 ".text" -> "markdown"
860 ".markdown" -> "markdown"
861 ".textile" -> "textile"
862 ".lhs" -> "markdown+lhs"
864 ".texinfo" -> "texinfo"
870 ".asciidoc" -> "asciidoc"
874 ['.',y] | y `elem` ['1'..'9'] -> "man"
880 rawArgs <- liftM (map UTF8.decodeArg) getArgs
882 let compatMode = (prg == "hsmarkdown")
884 let (actions, args, errors) = if compatMode
885 then ([], rawArgs, [])
886 else getOpt Permute options rawArgs
888 unless (null errors) $
889 err 2 $ concat $ errors ++
890 ["Try " ++ prg ++ " --help for more information."]
892 let defaultOpts' = if compatMode
893 then defaultOpts { optReader = "markdown_strict"
895 , optEmailObfuscation =
896 ReferenceObfuscation }
899 -- thread option data structure through all supplied option actions
900 opts <- foldl (>>=) (return defaultOpts') actions
902 let Opt { optTabStop = tabStop
903 , optPreserveTabs = preserveTabs
904 , optStandalone = standalone
905 , optReader = readerName
906 , optWriter = writerName
907 , optParseRaw = parseRaw
908 , optVariables = variables
909 , optMetadata = metadata
910 , optTableOfContents = toc
911 , optTransforms = transforms
912 , optTemplate = templatePath
913 , optOutputFile = outputFile
914 , optNumberSections = numberSections
915 , optNumberOffset = numberFrom
916 , optSectionDivs = sectionDivs
917 , optIncremental = incremental
918 , optSelfContained = selfContained
920 , optOldDashes = oldDashes
922 , optHtmlQTags = htmlQTags
923 , optHighlight = highlight
924 , optHighlightStyle = highlightStyle
925 , optChapters = chapters
926 , optHTMLMathMethod = mathMethod
927 , optReferenceODT = referenceODT
928 , optReferenceDocx = referenceDocx
929 , optEpubStylesheet = epubStylesheet
930 , optEpubMetadata = epubMetadata
931 , optEpubFonts = epubFonts
932 , optEpubChapterLevel = epubChapterLevel
933 , optTOCDepth = epubTOCDepth
934 , optDumpArgs = dumpArgs
935 , optIgnoreArgs = ignoreArgs
936 , optReferenceLinks = referenceLinks
938 , optColumns = columns
939 , optFilters = filters
940 , optEmailObfuscation = obfuscationMethod
941 , optIdentifierPrefix = idPrefix
942 , optIndentedCodeClasses = codeBlockClasses
943 , optDataDir = mbDataDir
944 , optCiteMethod = citeMethod
945 , optListings = listings
946 , optLaTeXEngine = latexEngine
947 , optSlideLevel = slideLevel
948 , optSetextHeaders = setextHeaders
950 , optTeXLigatures = texLigatures
951 , optDefaultImageExtension = defaultImageExtension
955 do UTF8.hPutStrLn stdout outputFile
956 mapM_ (\arg -> UTF8.hPutStrLn stdout arg) args
959 -- --bibliography implies -F pandoc-citeproc for backwards compatibility:
960 let filters' = case M.lookup "bibliography" metadata of
961 Just _ | all (\f -> takeBaseName f /= "pandoc-citeproc")
962 filters -> "pandoc-citeproc" : filters
964 let plugins = map externalFilter filters'
966 let sources = if ignoreArgs then [] else args
968 datadir <- case mbDataDir of
970 (liftM Just $ getAppUserDataDirectory "pandoc")
971 (\e -> let _ = (e :: E.SomeException)
973 Just _ -> return mbDataDir
975 -- assign reader and writer based on options and filenames
976 let readerName' = if null readerName
977 then let fallback = if any isURI sources
980 in defaultReaderName fallback sources
983 let writerName' = if null writerName
984 then defaultWriterName outputFile
985 else case writerName of
990 let pdfOutput = map toLower (takeExtension outputFile) == ".pdf"
992 let laTeXOutput = "latex" `isPrefixOf` writerName' ||
993 "beamer" `isPrefixOf` writerName'
996 -- make sure writer is latex or beamer
998 err 47 $ "cannot produce pdf output with " ++ writerName' ++ " writer"
999 -- check for latex program
1000 mbLatex <- findExecutable latexEngine
1003 latexEngine ++ " not found. " ++
1004 latexEngine ++ " is needed for pdf output."
1007 reader <- case getReader readerName' of
1011 let standalone' = standalone || not (isTextFormat writerName') || pdfOutput
1013 templ <- case templatePath of
1014 _ | not standalone' -> return ""
1016 deftemp <- getDefaultTemplate datadir writerName'
1021 -- strip off extensions
1022 let format = takeWhile (`notElem` "+-") writerName'
1023 let tp' = case takeExtension tp of
1026 E.catch (UTF8.readFile tp')
1027 (\e -> if isDoesNotExistError e
1029 (readDataFileUTF8 datadir
1030 ("templates" </> tp'))
1031 (\e' -> let _ = (e' :: E.SomeException)
1035 variables' <- case mathMethod of
1036 LaTeXMathML Nothing -> do
1037 s <- readDataFileUTF8 datadir
1039 return $ ("mathml-script", s) : variables
1040 MathML Nothing -> do
1041 s <- readDataFileUTF8 datadir
1043 return $ ("mathml-script", s) : variables
1044 _ -> return variables
1046 variables'' <- if "dzslides" `isPrefixOf` writerName'
1048 dztempl <- readDataFileUTF8 datadir
1049 ("dzslides" </> "template.html")
1050 let dzcore = unlines $ dropWhile (not . isPrefixOf "<!-- {{{{ dzslides core")
1052 return $ ("dzslides-core", dzcore) : variables'
1053 else return variables'
1054 let sourceURL = case sources of
1056 (x:_) -> case parseURI x of
1058 | uriScheme u `elem` ["http:","https:"] ->
1059 Just $ show u{ uriPath = "",
1064 let readerOpts = def{ readerSmart = smart || (texLigatures &&
1065 (laTeXOutput || "context" `isPrefixOf` writerName'))
1066 , readerStandalone = standalone'
1067 , readerParseRaw = parseRaw
1068 , readerColumns = columns
1069 , readerTabStop = tabStop
1070 , readerOldDashes = oldDashes
1071 , readerIndentedCodeClasses = codeBlockClasses
1072 , readerApplyMacros = not laTeXOutput
1073 , readerDefaultImageExtension = defaultImageExtension
1076 let writerOptions = def { writerStandalone = standalone',
1077 writerTemplate = templ,
1078 writerVariables = variables'',
1079 writerTabStop = tabStop,
1080 writerTableOfContents = toc,
1081 writerHTMLMathMethod = mathMethod,
1082 writerIncremental = incremental,
1083 writerCiteMethod = citeMethod,
1084 writerIgnoreNotes = False,
1085 writerNumberSections = numberSections,
1086 writerNumberOffset = numberFrom,
1087 writerSectionDivs = sectionDivs,
1088 writerReferenceLinks = referenceLinks,
1089 writerWrapText = wrap,
1090 writerColumns = columns,
1091 writerEmailObfuscation = obfuscationMethod,
1092 writerIdentifierPrefix = idPrefix,
1093 writerSourceURL = sourceURL,
1094 writerUserDataDir = datadir,
1095 writerHtml5 = html5,
1096 writerHtmlQTags = htmlQTags,
1097 writerChapters = chapters,
1098 writerListings = listings,
1099 writerBeamer = False,
1100 writerSlideLevel = slideLevel,
1101 writerHighlight = highlight,
1102 writerHighlightStyle = highlightStyle,
1103 writerSetextHeaders = setextHeaders,
1104 writerTeXLigatures = texLigatures,
1105 writerEpubMetadata = epubMetadata,
1106 writerEpubStylesheet = epubStylesheet,
1107 writerEpubFonts = epubFonts,
1108 writerEpubChapterLevel = epubChapterLevel,
1109 writerTOCDepth = epubTOCDepth,
1110 writerReferenceODT = referenceODT,
1111 writerReferenceDocx = referenceDocx
1114 when (not (isTextFormat writerName') && outputFile == "-") $
1115 err 5 $ "Cannot write " ++ writerName' ++ " output to stdout.\n" ++
1116 "Specify an output file using the -o option."
1118 let readSources [] = mapM readSource ["-"]
1119 readSources srcs = mapM readSource srcs
1120 readSource "-" = UTF8.getContents
1121 readSource src = case parseURI src of
1122 Just u | uriScheme u `elem` ["http:","https:"] ->
1124 _ -> UTF8.readFile src
1129 Right (bs,_) -> return $ UTF8.toString bs
1131 let convertTabs = tabFilter (if preserveTabs then 0 else tabStop)
1133 let handleIncludes' = if readerName' == "latex" || readerName' == "latex+lhs"
1137 doc <- readSources sources >>=
1138 handleIncludes' . convertTabs . intercalate "\n" >>=
1142 let doc0 = M.foldWithKey setMeta doc metadata
1143 let doc1 = foldr ($) doc0 transforms
1144 doc2 <- foldrM ($) doc1 $ map ($ [writerName']) plugins
1146 let writeBinary :: B.ByteString -> IO ()
1147 writeBinary = B.writeFile (UTF8.encodePath outputFile)
1149 let writerFn :: FilePath -> String -> IO ()
1150 writerFn "-" = UTF8.putStr
1151 writerFn f = UTF8.writeFile f
1153 case getWriter writerName' of
1155 Right (IOStringWriter f) -> f writerOptions doc2 >>= writerFn outputFile
1156 Right (IOByteStringWriter f) -> f writerOptions doc2 >>= writeBinary
1157 Right (PureStringWriter f)
1159 res <- makePDF latexEngine f writerOptions doc2
1161 Right pdf -> writeBinary pdf
1162 Left err' -> err 43 $ UTF8.toStringLazy err'
1163 | otherwise -> selfcontain (f writerOptions doc2 ++
1164 ['\n' | not standalone'])
1165 >>= writerFn outputFile . handleEntities
1166 where htmlFormat = writerName' `elem`
1167 ["html","html+lhs","html5","html5+lhs",
1168 "s5","slidy","slideous","dzslides","revealjs"]
1169 selfcontain = if selfContained && htmlFormat
1170 then makeSelfContained datadir
1172 handleEntities = if htmlFormat && ascii
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list