git.iko.soy/typeable/pandoc
on 4e12293 help sign in
655 lines 26 kB history raw
1{-# LANGUAGE CPP #-}
2{-
3Copyright (C) 2006-8 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
18-}
20{- |
21 Module : Main
22 Copyright : Copyright (C) 2006-8 John MacFarlane
23 License : GNU GPL, version 2 or above
25 Maintainer : John MacFarlane <jgm@berkeley@edu>
26 Stability : alpha
27 Portability : portable
29Parses command-line options and calls the appropriate readers and
30writers.
31-}
32module Main where
33import Text.Pandoc
34import Text.Pandoc.ODT
35import Text.Pandoc.Shared ( HTMLMathMethod (..) )
36import Text.Pandoc.Highlighting ( languages )
37import System.Environment ( getArgs, getProgName, getEnvironment )
38import System.Exit ( exitWith, ExitCode (..) )
39import System.FilePath ( takeExtension, takeDirectory )
40import System.Console.GetOpt
41import Data.Maybe ( fromMaybe )
42import Data.Char ( toLower )
43import Data.List ( intercalate )
44import Prelude hiding ( putStrLn, writeFile, readFile, getContents )
45import System.IO ( stdout, stderr )
46import System.IO.UTF8
47#ifdef _CITEPROC
48import Text.CSL
49import Text.Pandoc.Biblio
50#endif
52copyrightMessage :: String
53copyrightMessage = "\nCopyright (C) 2006-8 John MacFarlane\n" ++
54 "Web: http://johnmacfarlane.net/pandoc\n" ++
55 "This is free software; see the source for copying conditions. There is no\n" ++
56 "warranty, not even for merchantability or fitness for a particular purpose."
58compileInfo :: String
59compileInfo =
60#ifdef _CITEPROC
61 " +citeproc" ++
62#else
63 " -citeproc" ++
64#endif
65#ifdef _HIGHLIGHTING
66 " +highlighting" ++
67#else
68 " -highlighting" ++
69#endif
70 if null languages
71 then "\n"
72 else "\nCompiled with syntax highlighting support for:\n" ++ wrapWords 78 languages
74-- | Converts a list of strings into a single string with the items printed as
75-- comma separated words in lines with a maximum line length.
76wrapWords :: Int -> [String] -> String
77wrapWords c = wrap' c c where
78 wrap' _ _ [] = ""
79 wrap' cols remaining (x:xs) = if remaining == cols
80 then x ++ wrap' cols (remaining - length x) xs
81 else if (length x + 1) > remaining
82 then ",\n" ++ x ++ wrap' cols (cols - length x) xs
83 else ", " ++ x ++ wrap' cols (remaining - (length x + 2)) xs
85-- | Association list of formats and readers.
86readers :: [(String, ParserState -> String -> Pandoc)]
87readers = [("native" , readPandoc)
88 ,("markdown" , readMarkdown)
89 ,("rst" , readRST)
90 ,("html" , readHtml)
91 ,("latex" , readLaTeX)
92 ]
94-- | Reader for native Pandoc format.
95readPandoc :: ParserState -> String -> Pandoc
96readPandoc _ input = read input
98-- | Association list of formats and pairs of writers and default headers.
99writers :: [ ( String, ( WriterOptions -> Pandoc -> String, String ) ) ]
100writers = [("native" , (writeDoc, ""))
101 ,("html" , (writeHtmlString, ""))
102 ,("s5" , (writeS5String, defaultS5Header))
103 ,("docbook" , (writeDocbook, defaultDocbookHeader))
104 ,("opendocument" , (writeOpenDocument, defaultOpenDocumentHeader))
105 ,("odt" , (writeOpenDocument, defaultOpenDocumentHeader))
106 ,("latex" , (writeLaTeX, defaultLaTeXHeader))
107 ,("context" , (writeConTeXt, defaultConTeXtHeader))
108 ,("texinfo" , (writeTexinfo, ""))
109 ,("man" , (writeMan, ""))
110 ,("markdown" , (writeMarkdown, ""))
111 ,("rst" , (writeRST, ""))
112 ,("mediawiki" , (writeMediaWiki, ""))
113 ,("rtf" , (writeRTF, defaultRTFHeader))
114 ]
116isNonTextOutput :: String -> Bool
117isNonTextOutput = (`elem` ["odt"])
119-- | Writer for Pandoc native format.
120writeDoc :: WriterOptions -> Pandoc -> String
121writeDoc _ = prettyPandoc
123-- | Data structure for command line options.
124data Opt = Opt
125 { optPreserveTabs :: Bool -- ^ Convert tabs to spaces
126 , optTabStop :: Int -- ^ Number of spaces per tab
127 , optStandalone :: Bool -- ^ Include header, footer
128 , optReader :: String -- ^ Reader format
129 , optWriter :: String -- ^ Writer format
130 , optParseRaw :: Bool -- ^ Parse unconvertable HTML and TeX
131 , optCSS :: [String] -- ^ CSS file to link to
132 , optTableOfContents :: Bool -- ^ Include table of contents
133 , optIncludeInHeader :: String -- ^ File to include in header
134 , optIncludeBeforeBody :: String -- ^ File to include at top of body
135 , optIncludeAfterBody :: String -- ^ File to include at end of body
136 , optCustomHeader :: String -- ^ Custom header to use, or "DEFAULT"
137 , optTitlePrefix :: String -- ^ Optional prefix for HTML title
138 , optOutputFile :: String -- ^ Name of output file
139 , optNumberSections :: Bool -- ^ Number sections in LaTeX
140 , optIncremental :: Bool -- ^ Use incremental lists in S5
141 , optSmart :: Bool -- ^ Use smart typography
142 , optHTMLMathMethod :: HTMLMathMethod -- ^ Method to print HTML math
143 , optDumpArgs :: Bool -- ^ Output command-line arguments
144 , optIgnoreArgs :: Bool -- ^ Ignore command-line arguments
145 , optStrict :: Bool -- ^ Use strict markdown syntax
146 , optReferenceLinks :: Bool -- ^ Use reference links in writing markdown, rst
147 , optWrapText :: Bool -- ^ Wrap text
148 , optSanitizeHTML :: Bool -- ^ Sanitize HTML
149 , optLHSIn :: Bool -- ^ Treat input as literate haskell
150 , optLHSOut :: Bool -- ^ Write output as literate haskell
151#ifdef _CITEPROC
152 , optModsFile :: String
153 , optCslFile :: String
154#endif
155 }
157-- | Defaults for command-line options.
158defaultOpts :: Opt
159defaultOpts = Opt
160 { optPreserveTabs = False
161 , optTabStop = 4
162 , optStandalone = False
163 , optReader = "" -- null for default reader
164 , optWriter = "" -- null for default writer
165 , optParseRaw = False
166 , optCSS = []
167 , optTableOfContents = False
168 , optIncludeInHeader = ""
169 , optIncludeBeforeBody = ""
170 , optIncludeAfterBody = ""
171 , optCustomHeader = "DEFAULT"
172 , optTitlePrefix = ""
173 , optOutputFile = "-" -- "-" means stdout
174 , optNumberSections = False
175 , optIncremental = False
176 , optSmart = False
177 , optHTMLMathMethod = PlainMath
178 , optDumpArgs = False
179 , optIgnoreArgs = False
180 , optStrict = False
181 , optReferenceLinks = False
182 , optWrapText = True
183 , optSanitizeHTML = False
184 , optLHSIn = False
185 , optLHSOut = False
186#ifdef _CITEPROC
187 , optModsFile = []
188 , optCslFile = []
189#endif
190 }
192-- | A list of functions, each transforming the options data structure
193-- in response to a command-line option.
194options :: [OptDescr (Opt -> IO Opt)]
195options =
196 [ Option "fr" ["from","read"]
197 (ReqArg
198 (\arg opt -> return opt { optReader = map toLower arg })
199 "FORMAT")
200 "" -- ("(" ++ (intercalate ", " $ map fst readers) ++ ")")
202 , Option "tw" ["to","write"]
203 (ReqArg
204 (\arg opt -> return opt { optWriter = map toLower arg })
205 "FORMAT")
206 "" -- ("(" ++ (intercalate ", " $ map fst writers) ++ ")")
208 , Option "s" ["standalone"]
209 (NoArg
210 (\opt -> return opt { optStandalone = True }))
211 "" -- "Include needed header and footer on output"
213 , Option "o" ["output"]
214 (ReqArg
215 (\arg opt -> return opt { optOutputFile = arg })
216 "FILENAME")
217 "" -- "Name of output file"
219 , Option "p" ["preserve-tabs"]
220 (NoArg
221 (\opt -> return opt { optPreserveTabs = True }))
222 "" -- "Preserve tabs instead of converting to spaces"
224 , Option "" ["tab-stop"]
225 (ReqArg
226 (\arg opt -> return opt { optTabStop = (read arg) } )
227 "TABSTOP")
228 "" -- "Tab stop (default 4)"
230 , Option "" ["strict"]
231 (NoArg
232 (\opt -> return opt { optStrict = True } ))
233 "" -- "Disable markdown syntax extensions"
235 , Option "" ["reference-links"]
236 (NoArg
237 (\opt -> return opt { optReferenceLinks = True } ))
238 "" -- "Use reference links in parsing HTML"
240 , Option "R" ["parse-raw"]
241 (NoArg
242 (\opt -> return opt { optParseRaw = True }))
243 "" -- "Parse untranslatable HTML codes and LaTeX environments as raw"
245 , Option "S" ["smart"]
246 (NoArg
247 (\opt -> return opt { optSmart = True }))
248 "" -- "Use smart quotes, dashes, and ellipses"
250 , Option "m" ["latexmathml", "asciimathml"]
251 (OptArg
252 (\arg opt -> return opt { optHTMLMathMethod =
253 LaTeXMathML arg })
254 "URL")
255 "" -- "Use LaTeXMathML script in html output"
257 , Option "" ["mimetex"]
258 (OptArg
259 (\arg opt -> return opt { optHTMLMathMethod = MimeTeX
260 (fromMaybe "/cgi-bin/mimetex.cgi" arg)})
261 "URL")
262 "" -- "Use mimetex for HTML math"
264 , Option "" ["jsmath"]
265 (OptArg
266 (\arg opt -> return opt { optHTMLMathMethod = JsMath arg})
267 "URL")
268 "" -- "Use jsMath for HTML math"
270 , Option "" ["gladtex"]
271 (NoArg
272 (\opt -> return opt { optHTMLMathMethod = GladTeX }))
273 "" -- "Use gladtex for HTML math"
275 , Option "i" ["incremental"]
276 (NoArg
277 (\opt -> return opt { optIncremental = True }))
278 "" -- "Make list items display incrementally in S5"
280 , Option "N" ["number-sections"]
281 (NoArg
282 (\opt -> return opt { optNumberSections = True }))
283 "" -- "Number sections in LaTeX"
285 , Option "" ["no-wrap"]
286 (NoArg
287 (\opt -> return opt { optWrapText = False }))
288 "" -- "Do not wrap text in output"
290 , Option "" ["sanitize-html"]
291 (NoArg
292 (\opt -> return opt { optSanitizeHTML = True }))
293 "" -- "Sanitize HTML"
295 , Option "" ["lhs-in"]
296 (NoArg
297 (\opt -> return opt { optLHSIn = True }))
298 "" -- "Treat input as literate haskell"
300 , Option "" ["lhs-out"]
301 (NoArg
302 (\opt -> return opt { optLHSOut = True }))
303 "" -- "Write output as literate haskell"
305 , Option "" ["lhs"]
306 (NoArg
307 (\opt -> return opt { optLHSIn = True,
308 optLHSOut = True }))
309 "" -- "Equivalent to --lhs-in --lhs-out"
311 , Option "" ["toc", "table-of-contents"]
312 (NoArg
313 (\opt -> return opt { optTableOfContents = True }))
314 "" -- "Include table of contents"
316 , Option "c" ["css"]
317 (ReqArg
318 (\arg opt -> do
319 let old = optCSS opt
320 return opt { optCSS = old ++ [arg],
321 optStandalone = True })
322 "CSS")
323 "" -- "Link to CSS style sheet"
325 , Option "H" ["include-in-header"]
326 (ReqArg
327 (\arg opt -> do
328 let old = optIncludeInHeader opt
329 text <- readFile arg
330 return opt { optIncludeInHeader = old ++ text,
331 optStandalone = True })
332 "FILENAME")
333 "" -- "File to include at end of header (implies -s)"
335 , Option "B" ["include-before-body"]
336 (ReqArg
337 (\arg opt -> do
338 let old = optIncludeBeforeBody opt
339 text <- readFile arg
340 return opt { optIncludeBeforeBody = old ++ text })
341 "FILENAME")
342 "" -- "File to include before document body"
344 , Option "A" ["include-after-body"]
345 (ReqArg
346 (\arg opt -> do
347 let old = optIncludeAfterBody opt
348 text <- readFile arg
349 return opt { optIncludeAfterBody = old ++ text })
350 "FILENAME")
351 "" -- "File to include after document body"
353 , Option "C" ["custom-header"]
354 (ReqArg
355 (\arg opt -> do
356 text <- readFile arg
357 return opt { optCustomHeader = text,
358 optStandalone = True })
359 "FILENAME")
360 "" -- "File to use for custom header (implies -s)"
362 , Option "T" ["title-prefix"]
363 (ReqArg
364 (\arg opt -> return opt { optTitlePrefix = arg,
365 optStandalone = True })
366 "STRING")
367 "" -- "String to prefix to HTML window title"
369 , Option "D" ["print-default-header"]
370 (ReqArg
371 (\arg _ -> do
372 let header = case (lookup arg writers) of
373 Just (_, h) -> h
374 Nothing -> error ("Unknown reader: " ++ arg)
375 hPutStr stdout header
376 exitWith ExitSuccess)
377 "FORMAT")
378 "" -- "Print default header for FORMAT"
379#ifdef _CITEPROC
380 , Option "" ["mods"]
381 (ReqArg
382 (\arg opt -> return opt { optModsFile = arg} )
383 "FILENAME")
384 ""
385 , Option "" ["csl"]
386 (ReqArg
387 (\arg opt -> return opt { optCslFile = arg} )
388 "FILENAME")
389 ""
390#endif
391 , Option "" ["dump-args"]
392 (NoArg
393 (\opt -> return opt { optDumpArgs = True }))
394 "" -- "Print output filename and arguments to stdout."
396 , Option "" ["ignore-args"]
397 (NoArg
398 (\opt -> return opt { optIgnoreArgs = True }))
399 "" -- "Ignore command-line arguments."
401 , Option "v" ["version"]
402 (NoArg
403 (\_ -> do
404 prg <- getProgName
405 hPutStrLn stderr (prg ++ " " ++ pandocVersion ++ compileInfo ++
406 copyrightMessage)
407 exitWith $ ExitFailure 4))
408 "" -- "Print version"
410 , Option "h" ["help"]
411 (NoArg
412 (\_ -> do
413 prg <- getProgName
414 hPutStr stderr (usageMessage prg options)
415 exitWith $ ExitFailure 2))
416 "" -- "Show help"
417 ]
419-- Returns usage message
420usageMessage :: String -> [OptDescr (Opt -> IO Opt)] -> String
421usageMessage programName opts = usageInfo
422 (programName ++ " [OPTIONS] [FILES]" ++ "\nInput formats: " ++
423 (intercalate ", " $ map fst readers) ++ "\nOutput formats: " ++
424 (intercalate ", " $ map fst writers) ++ "\nOptions:")
425 opts
427-- Determine default reader based on source file extensions
428defaultReaderName :: [FilePath] -> String
429defaultReaderName [] = "markdown"
430defaultReaderName (x:xs) =
431 case takeExtension (map toLower x) of
432 ".xhtml" -> "html"
433 ".html" -> "html"
434 ".htm" -> "html"
435 ".tex" -> "latex"
436 ".latex" -> "latex"
437 ".ltx" -> "latex"
438 ".rst" -> "rst"
439 ".native" -> "native"
440 _ -> defaultReaderName xs
442-- Returns True if extension of first source is .lhs
443lhsExtension :: [FilePath] -> Bool
444lhsExtension (x:_) = takeExtension x == ".lhs"
445lhsExtension _ = False
447-- Determine default writer based on output file extension
448defaultWriterName :: FilePath -> String
449defaultWriterName "-" = "html" -- no output file
450defaultWriterName x =
451 case takeExtension (map toLower x) of
452 "" -> "markdown" -- empty extension
453 ".tex" -> "latex"
454 ".latex" -> "latex"
455 ".ltx" -> "latex"
456 ".context" -> "context"
457 ".ctx" -> "context"
458 ".rtf" -> "rtf"
459 ".rst" -> "rst"
460 ".s5" -> "s5"
461 ".native" -> "native"
462 ".txt" -> "markdown"
463 ".text" -> "markdown"
464 ".md" -> "markdown"
465 ".markdown" -> "markdown"
466 ".texi" -> "texinfo"
467 ".texinfo" -> "texinfo"
468 ".db" -> "docbook"
469 ".odt" -> "odt"
470 ['.',y] | y `elem` ['1'..'9'] -> "man"
471 _ -> "html"
473main :: IO ()
474main = do
476 rawArgs <- getArgs
477 prg <- getProgName
478 let compatMode = (prg == "hsmarkdown")
480 let (actions, args, errors) = if compatMode
481 then ([], rawArgs, [])
482 else getOpt Permute options rawArgs
484 if (not (null errors))
485 then do
486 name <- getProgName
487 mapM (\e -> hPutStrLn stderr e) errors
488 hPutStr stderr (usageMessage name options)
489 exitWith $ ExitFailure 2
490 else
491 return ()
493 let defaultOpts' = if compatMode
494 then defaultOpts { optReader = "markdown"
495 , optWriter = "html"
496 , optStrict = True }
497 else defaultOpts
499 -- thread option data structure through all supplied option actions
500 opts <- foldl (>>=) (return defaultOpts') actions
502 let Opt { optPreserveTabs = preserveTabs
503 , optTabStop = tabStop
504 , optStandalone = standalone
505 , optReader = readerName
506 , optWriter = writerName
507 , optParseRaw = parseRaw
508 , optCSS = css
509 , optTableOfContents = toc
510 , optIncludeInHeader = includeHeader
511 , optIncludeBeforeBody = includeBefore
512 , optIncludeAfterBody = includeAfter
513 , optCustomHeader = customHeader
514 , optTitlePrefix = titlePrefix
515 , optOutputFile = outputFile
516 , optNumberSections = numberSections
517 , optIncremental = incremental
518 , optSmart = smart
519 , optHTMLMathMethod = mathMethod
520 , optDumpArgs = dumpArgs
521 , optIgnoreArgs = ignoreArgs
522 , optStrict = strict
523 , optReferenceLinks = referenceLinks
524 , optWrapText = wrap
525 , optSanitizeHTML = sanitize
526 , optLHSIn = lhsIn
527 , optLHSOut = lhsOut
528#ifdef _CITEPROC
529 , optModsFile = modsFile
530 , optCslFile = cslFile
531#endif
532 } = opts
534 if dumpArgs
535 then do
536 hPutStrLn stdout outputFile
537 mapM (\arg -> hPutStrLn stdout arg) args
538 exitWith $ ExitSuccess
539 else return ()
541 let sources = if ignoreArgs then [] else args
543 -- assign reader and writer based on options and filenames
544 let readerName' = if null readerName
545 then defaultReaderName sources
546 else readerName
548 let writerName' = if null writerName
549 then defaultWriterName outputFile
550 else writerName
552 reader <- case (lookup readerName' readers) of
553 Just r -> return r
554 Nothing -> error ("Unknown reader: " ++ readerName')
556 (writer, defaultHeader) <- case (lookup writerName' writers) of
557 Just (w,h) -> return (w, h)
558 Nothing -> error ("Unknown writer: " ++ writerName')
560 environment <- getEnvironment
561 let columns = case lookup "COLUMNS" environment of
562 Just cols -> read cols
563 Nothing -> stateColumns defaultParserState
565 let tabFilter _ [] = ""
566 tabFilter _ ('\n':xs) = '\n':(tabFilter tabStop xs)
567 -- remove DOS line endings
568 tabFilter _ ('\r':'\n':xs) = '\n':(tabFilter tabStop xs)
569 tabFilter _ ('\r':xs) = '\n':(tabFilter tabStop xs)
570 tabFilter spsToNextStop ('\t':xs) =
571 if preserveTabs
572 then '\t':(tabFilter tabStop xs)
573 else replicate spsToNextStop ' ' ++ tabFilter tabStop xs
574 tabFilter 1 (x:xs) =
575 x:(tabFilter tabStop xs)
576 tabFilter spsToNextStop (x:xs) =
577 x:(tabFilter (spsToNextStop - 1) xs)
579 let standalone' = (standalone && not strict) || isNonTextOutput writerName'
581#ifdef _CITEPROC
582 refs <- if null modsFile then return [] else readModsColletionFile modsFile
583#endif
585 let startParserState =
586 defaultParserState { stateParseRaw = parseRaw,
587 stateTabStop = tabStop,
588 stateSanitizeHTML = sanitize,
589 stateLiterateHaskell = lhsIn ||
590 lhsExtension sources,
591 stateStandalone = standalone',
592#ifdef _CITEPROC
593 stateCitations = map citeKey refs,
594#endif
595 stateSmart = smart || writerName' `elem`
596 ["latex", "context"],
597 stateColumns = columns,
598 stateStrict = strict }
599 let csslink = if null css
600 then ""
601 else concatMap
602 (\f -> "<link rel=\"stylesheet\" href=\"" ++
603 f ++ "\" type=\"text/css\" media=\"all\" />\n")
604 css
605 let header = (if (customHeader == "DEFAULT")
606 then defaultHeader
607 else customHeader) ++ csslink ++ includeHeader
608 let writerOptions = WriterOptions { writerStandalone = standalone',
609 writerHeader = header,
610 writerTitlePrefix = titlePrefix,
611 writerTabStop = tabStop,
612 writerTableOfContents = toc &&
613 (not strict) &&
614 writerName' /= "s5",
615 writerHTMLMathMethod = mathMethod,
616 writerS5 = (writerName' == "s5"),
617 writerIgnoreNotes = False,
618 writerIncremental = incremental,
619 writerNumberSections = numberSections,
620 writerIncludeBefore = includeBefore,
621 writerIncludeAfter = includeAfter,
622 writerStrictMarkdown = strict,
623 writerReferenceLinks = referenceLinks,
624 writerWrapText = wrap,
625 writerLiterateHaskell = lhsOut ||
626 lhsExtension [outputFile] }
628 if isNonTextOutput writerName' && outputFile == "-"
629 then do hPutStrLn stderr ("Error: Cannot write " ++ writerName ++ " output to stdout.\n" ++
630 "Specify an output file using the -o option.")
631 exitWith $ ExitFailure 5
632 else return ()
634 let sourceDirRelative = if null sources
635 then ""
636 else takeDirectory (head sources)
638 let writeOutput = case writerName' of
639 "odt" -> saveOpenDocumentAsODT outputFile sourceDirRelative
640 _ -> if outputFile == "-"
641 then putStrLn
642 else writeFile outputFile . (++ "\n")
644 fmap (reader startParserState . tabFilter tabStop . intercalate "\n")
645 (readSources sources) >>=
646#ifdef _CITEPROC
647 processBiblio cslFile refs >>=
648#endif
649 writeOutput . writer writerOptions
651 where
652 readSources [] = mapM readSource ["-"]
653 readSources sources = mapM readSource sources
654 readSource "-" = getContents
655 readSource source = readFile source