git.iko.soy/typeable/pandoc
on b9e30ca help sign in
531 lines 22 kB history raw
1{-
2Copyright (C) 2006-7 John MacFarlane <jgm@berkeley.edu>
4This program is free software; you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation; either version 2 of the License, or
7(at your option) any later version.
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
14You should have received a copy of the GNU General Public License
15along with this program; if not, write to the Free Software
16Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17-}
19{- |
20 Module : Main
21 Copyright : Copyright (C) 2006-7 John MacFarlane
22 License : GNU GPL, version 2 or above
24 Maintainer : John MacFarlane <jgm@berkeley@edu>
25 Stability : alpha
26 Portability : portable
28Parses command-line options and calls the appropriate readers and
29writers.
30-}
31module Main where
32import Text.Pandoc
33import Text.Pandoc.UTF8
34import Text.Pandoc.Shared ( joinWithSep, HTMLMathMethod (..) )
35import Text.Regex ( mkRegex, matchRegex )
36import System.Environment ( getArgs, getProgName, getEnvironment )
37import System.Exit ( exitWith, ExitCode (..) )
38import System.Console.GetOpt
39import System.IO
40import Data.Maybe ( fromMaybe )
41import Data.List ( isPrefixOf )
42import Data.Char ( toLower )
43import Control.Monad ( (>>=) )
45copyrightMessage :: String
46copyrightMessage = "\nCopyright (C) 2006-7 John MacFarlane\n\
47 \Web: http://johnmacfarlane.net/pandoc\n\
48 \This is free software; see the source for copying conditions. There is no\n\
49 \warranty, not even for merchantability or fitness for a particular purpose."
51-- | Association list of formats and readers.
52readers :: [(String, ParserState -> String -> Pandoc)]
53readers = [("native" , readPandoc)
54 ,("markdown" , readMarkdown)
55 ,("rst" , readRST)
56 ,("html" , readHtml)
57 ,("latex" , readLaTeX)
58 ]
60-- | Reader for native Pandoc format.
61readPandoc :: ParserState -> String -> Pandoc
62readPandoc state input = read input
64-- | Association list of formats and pairs of writers and default headers.
65writers :: [ ( String, ( WriterOptions -> Pandoc -> String, String ) ) ]
66writers = [("native" , (writeDoc, ""))
67 ,("html" , (writeHtmlString, ""))
68 ,("s5" , (writeS5String, defaultS5Header))
69 ,("docbook" , (writeDocbook, defaultDocbookHeader))
70 ,("latex" , (writeLaTeX, defaultLaTeXHeader))
71 ,("context" , (writeConTeXt, defaultConTeXtHeader))
72 ,("man" , (writeMan, ""))
73 ,("markdown" , (writeMarkdown, ""))
74 ,("rst" , (writeRST, ""))
75 ,("rtf" , (writeRTF, defaultRTFHeader))
76 ]
78-- | Writer for Pandoc native format.
79writeDoc :: WriterOptions -> Pandoc -> String
80writeDoc options = prettyPandoc
82-- | Data structure for command line options.
83data Opt = Opt
84 { optPreserveTabs :: Bool -- ^ Convert tabs to spaces
85 , optTabStop :: Int -- ^ Number of spaces per tab
86 , optStandalone :: Bool -- ^ Include header, footer
87 , optReader :: String -- ^ Reader format
88 , optWriter :: String -- ^ Writer format
89 , optParseRaw :: Bool -- ^ Parse unconvertable HTML and TeX
90 , optCSS :: String -- ^ CSS file to link to
91 , optTableOfContents :: Bool -- ^ Include table of contents
92 , optIncludeInHeader :: String -- ^ File to include in header
93 , optIncludeBeforeBody :: String -- ^ File to include at top of body
94 , optIncludeAfterBody :: String -- ^ File to include at end of body
95 , optCustomHeader :: String -- ^ Custom header to use, or "DEFAULT"
96 , optTitlePrefix :: String -- ^ Optional prefix for HTML title
97 , optOutputFile :: String -- ^ Name of output file
98 , optNumberSections :: Bool -- ^ Number sections in LaTeX
99 , optIncremental :: Bool -- ^ Use incremental lists in S5
100 , optSmart :: Bool -- ^ Use smart typography
101 , optHTMLMathMethod :: HTMLMathMethod -- ^ Method to print HTML math
102 , optDumpArgs :: Bool -- ^ Output command-line arguments
103 , optIgnoreArgs :: Bool -- ^ Ignore command-line arguments
104 , optStrict :: Bool -- ^ Use strict markdown syntax
105 , optReferenceLinks :: Bool -- ^ Use reference links in writing markdown, rst
106 , optWrapText :: Bool -- ^ Wrap text
107 , optSanitizeHTML :: Bool -- ^ Sanitize HTML
108 }
110-- | Defaults for command-line options.
111defaultOpts :: Opt
112defaultOpts = Opt
113 { optPreserveTabs = False
114 , optTabStop = 4
115 , optStandalone = False
116 , optReader = "" -- null for default reader
117 , optWriter = "" -- null for default writer
118 , optParseRaw = False
119 , optCSS = ""
120 , optTableOfContents = False
121 , optIncludeInHeader = ""
122 , optIncludeBeforeBody = ""
123 , optIncludeAfterBody = ""
124 , optCustomHeader = "DEFAULT"
125 , optTitlePrefix = ""
126 , optOutputFile = "-" -- "-" means stdout
127 , optNumberSections = False
128 , optIncremental = False
129 , optSmart = False
130 , optHTMLMathMethod = PlainMath
131 , optDumpArgs = False
132 , optIgnoreArgs = False
133 , optStrict = False
134 , optReferenceLinks = False
135 , optWrapText = True
136 , optSanitizeHTML = False
137 }
139-- | A list of functions, each transforming the options data structure
140-- in response to a command-line option.
141options :: [OptDescr (Opt -> IO Opt)]
142options =
143 [ Option "fr" ["from","read"]
144 (ReqArg
145 (\arg opt -> return opt { optReader = map toLower arg })
146 "FORMAT")
147 "" -- ("(" ++ (joinWithSep ", " $ map fst readers) ++ ")")
149 , Option "tw" ["to","write"]
150 (ReqArg
151 (\arg opt -> return opt { optWriter = map toLower arg })
152 "FORMAT")
153 "" -- ("(" ++ (joinWithSep ", " $ map fst writers) ++ ")")
155 , Option "s" ["standalone"]
156 (NoArg
157 (\opt -> return opt { optStandalone = True }))
158 "" -- "Include needed header and footer on output"
160 , Option "o" ["output"]
161 (ReqArg
162 (\arg opt -> return opt { optOutputFile = arg })
163 "FILENAME")
164 "" -- "Name of output file"
166 , Option "p" ["preserve-tabs"]
167 (NoArg
168 (\opt -> return opt { optPreserveTabs = True }))
169 "" -- "Preserve tabs instead of converting to spaces"
171 , Option "" ["tab-stop"]
172 (ReqArg
173 (\arg opt -> return opt { optTabStop = (read arg) } )
174 "TABSTOP")
175 "" -- "Tab stop (default 4)"
177 , Option "" ["strict"]
178 (NoArg
179 (\opt -> return opt { optStrict = True } ))
180 "" -- "Disable markdown syntax extensions"
182 , Option "" ["reference-links"]
183 (NoArg
184 (\opt -> return opt { optReferenceLinks = True } ))
185 "" -- "Use reference links in parsing HTML"
187 , Option "R" ["parse-raw"]
188 (NoArg
189 (\opt -> return opt { optParseRaw = True }))
190 "" -- "Parse untranslatable HTML codes and LaTeX environments as raw"
192 , Option "S" ["smart"]
193 (NoArg
194 (\opt -> return opt { optSmart = True }))
195 "" -- "Use smart quotes, dashes, and ellipses"
197 , Option "m" ["asciimathml"]
198 (OptArg
199 (\arg opt -> return opt { optHTMLMathMethod =
200 ASCIIMathML arg })
201 "URL")
202 "" -- "Use ASCIIMathML script in html output"
204 , Option "" ["mimetex"]
205 (OptArg
206 (\arg opt -> return opt { optHTMLMathMethod = MimeTeX
207 (fromMaybe "/cgi-bin/mimetex.cgi" arg)})
208 "URL")
209 "" -- "Use mimetex for HTML math"
211 , Option "" ["gladtex"]
212 (NoArg
213 (\opt -> return opt { optHTMLMathMethod = GladTeX }))
214 "" -- "Use gladtex for HTML math"
216 , Option "i" ["incremental"]
217 (NoArg
218 (\opt -> return opt { optIncremental = True }))
219 "" -- "Make list items display incrementally in S5"
221 , Option "N" ["number-sections"]
222 (NoArg
223 (\opt -> return opt { optNumberSections = True }))
224 "" -- "Number sections in LaTeX"
226 , Option "" ["no-wrap"]
227 (NoArg
228 (\opt -> return opt { optWrapText = False }))
229 "" -- "Do not wrap text in output"
231 , Option "" ["sanitize-html"]
232 (NoArg
233 (\opt -> return opt { optSanitizeHTML = True }))
234 "" -- "Sanitize HTML"
236 , Option "" ["toc", "table-of-contents"]
237 (NoArg
238 (\opt -> return opt { optTableOfContents = True }))
239 "" -- "Include table of contents"
241 , Option "c" ["css"]
242 (ReqArg
243 (\arg opt -> return opt { optCSS = arg,
244 optStandalone = True })
245 "CSS")
246 "" -- "Link to CSS style sheet"
248 , Option "H" ["include-in-header"]
249 (ReqArg
250 (\arg opt -> do
251 let old = optIncludeInHeader opt
252 text <- readFile arg
253 return opt { optIncludeInHeader = old ++ fromUTF8 text,
254 optStandalone = True })
255 "FILENAME")
256 "" -- "File to include at end of header (implies -s)"
258 , Option "B" ["include-before-body"]
259 (ReqArg
260 (\arg opt -> do
261 let old = optIncludeBeforeBody opt
262 text <- readFile arg
263 return opt { optIncludeBeforeBody = old ++ fromUTF8 text })
264 "FILENAME")
265 "" -- "File to include before document body"
267 , Option "A" ["include-after-body"]
268 (ReqArg
269 (\arg opt -> do
270 let old = optIncludeAfterBody opt
271 text <- readFile arg
272 return opt { optIncludeAfterBody = old ++ fromUTF8 text })
273 "FILENAME")
274 "" -- "File to include after document body"
276 , Option "C" ["custom-header"]
277 (ReqArg
278 (\arg opt -> do
279 text <- readFile arg
280 return opt { optCustomHeader = fromUTF8 text,
281 optStandalone = True })
282 "FILENAME")
283 "" -- "File to use for custom header (implies -s)"
285 , Option "T" ["title-prefix"]
286 (ReqArg
287 (\arg opt -> return opt { optTitlePrefix = arg,
288 optStandalone = True })
289 "STRING")
290 "" -- "String to prefix to HTML window title"
292 , Option "D" ["print-default-header"]
293 (ReqArg
294 (\arg opt -> do
295 let header = case (lookup arg writers) of
296 Just (writer, head) -> head
297 Nothing -> error ("Unknown reader: " ++ arg)
298 hPutStr stdout header
299 exitWith ExitSuccess)
300 "FORMAT")
301 "" -- "Print default header for FORMAT"
303 , Option "" ["dump-args"]
304 (NoArg
305 (\opt -> return opt { optDumpArgs = True }))
306 "" -- "Print output filename and arguments to stdout."
308 , Option "" ["ignore-args"]
309 (NoArg
310 (\opt -> return opt { optIgnoreArgs = True }))
311 "" -- "Ignore command-line arguments."
313 , Option "v" ["version"]
314 (NoArg
315 (\_ -> do
316 prg <- getProgName
317 hPutStrLn stderr (prg ++ " " ++ pandocVersion ++
318 copyrightMessage)
319 exitWith $ ExitFailure 4))
320 "" -- "Print version"
322 , Option "h" ["help"]
323 (NoArg
324 (\_ -> do
325 prg <- getProgName
326 hPutStr stderr (usageMessage prg options)
327 exitWith $ ExitFailure 2))
328 "" -- "Show help"
329 ]
331-- Returns usage message
332usageMessage :: String -> [OptDescr (Opt -> IO Opt)] -> String
333usageMessage programName options = usageInfo
334 (programName ++ " [OPTIONS] [FILES]" ++ "\nInput formats: " ++
335 (joinWithSep ", " $ map fst readers) ++ "\nOutput formats: " ++
336 (joinWithSep ", " $ map fst writers) ++ "\nOptions:")
337 options
339-- Determine default reader based on source file extensions
340defaultReaderName :: [String] -> String
341defaultReaderName [] = "markdown"
342defaultReaderName (x:xs) =
343 let x' = map toLower x in
344 case (matchRegex (mkRegex ".*\\.(.*)") x') of
345 Nothing -> defaultReaderName xs -- no extension
346 Just ["xhtml"] -> "html"
347 Just ["html"] -> "html"
348 Just ["htm"] -> "html"
349 Just ["tex"] -> "latex"
350 Just ["latex"] -> "latex"
351 Just ["ltx"] -> "latex"
352 Just ["rst"] -> "rst"
353 Just ["native"] -> "native"
354 Just _ -> "markdown"
356-- Determine default writer based on output file extension
357defaultWriterName :: String -> String
358defaultWriterName "-" = "html" -- no output file
359defaultWriterName x =
360 let x' = map toLower x in
361 case (matchRegex (mkRegex ".*\\.(.*)") x') of
362 Nothing -> "markdown" -- no extension
363 Just [""] -> "markdown" -- empty extension
364 Just ["tex"] -> "latex"
365 Just ["latex"] -> "latex"
366 Just ["ltx"] -> "latex"
367 Just ["context"] -> "context"
368 Just ["ctx"] -> "context"
369 Just ["rtf"] -> "rtf"
370 Just ["rst"] -> "rst"
371 Just ["s5"] -> "s5"
372 Just ["native"] -> "native"
373 Just ["txt"] -> "markdown"
374 Just ["text"] -> "markdown"
375 Just ["md"] -> "markdown"
376 Just ["markdown"] -> "markdown"
377 Just ["db"] -> "docbook"
378 Just ["xml"] -> "docbook"
379 Just ["sgml"] -> "docbook"
380 Just [[x]] | x `elem` ['1'..'9'] -> "man"
381 Just _ -> "html"
383main = do
385 rawArgs <- getArgs
386 prg <- getProgName
387 let compatMode = (prg == "hsmarkdown")
389 let (actions, args, errors) = if compatMode
390 then ([], rawArgs, [])
391 else getOpt Permute options rawArgs
393 if (not (null errors))
394 then do
395 name <- getProgName
396 mapM (\e -> hPutStrLn stderr e) errors
397 hPutStr stderr (usageMessage name options)
398 exitWith $ ExitFailure 2
399 else
400 return ()
402 let defaultOpts' = if compatMode
403 then defaultOpts { optReader = "markdown"
404 , optWriter = "html"
405 , optStrict = True }
406 else defaultOpts
408 -- thread option data structure through all supplied option actions
409 opts <- foldl (>>=) (return defaultOpts') actions
411 let Opt { optPreserveTabs = preserveTabs
412 , optTabStop = tabStop
413 , optStandalone = standalone
414 , optReader = readerName
415 , optWriter = writerName
416 , optParseRaw = parseRaw
417 , optCSS = css
418 , optTableOfContents = toc
419 , optIncludeInHeader = includeHeader
420 , optIncludeBeforeBody = includeBefore
421 , optIncludeAfterBody = includeAfter
422 , optCustomHeader = customHeader
423 , optTitlePrefix = titlePrefix
424 , optOutputFile = outputFile
425 , optNumberSections = numberSections
426 , optIncremental = incremental
427 , optSmart = smart
428 , optHTMLMathMethod = mathMethod
429 , optDumpArgs = dumpArgs
430 , optIgnoreArgs = ignoreArgs
431 , optStrict = strict
432 , optReferenceLinks = referenceLinks
433 , optWrapText = wrap
434 , optSanitizeHTML = sanitize
435 } = opts
437 if dumpArgs
438 then do
439 hPutStrLn stdout outputFile
440 mapM (\arg -> hPutStrLn stdout arg) args
441 exitWith $ ExitSuccess
442 else return ()
444 let sources = if ignoreArgs then [] else args
446 -- assign reader and writer based on options and filenames
447 let readerName' = if null readerName
448 then defaultReaderName sources
449 else readerName
451 let writerName' = if null writerName
452 then defaultWriterName outputFile
453 else writerName
455 reader <- case (lookup readerName' readers) of
456 Just r -> return r
457 Nothing -> error ("Unknown reader: " ++ readerName')
459 (writer, defaultHeader) <- case (lookup writerName' writers) of
460 Just (w,h) -> return (w, h)
461 Nothing -> error ("Unknown writer: " ++ writerName')
463 output <- if (outputFile == "-")
464 then return stdout
465 else openFile outputFile WriteMode
467 environment <- getEnvironment
468 let columns = case lookup "COLUMNS" environment of
469 Just cols -> read cols
470 Nothing -> stateColumns defaultParserState
472 let tabFilter _ [] = ""
473 tabFilter _ ('\n':xs) = '\n':(tabFilter tabStop xs)
474 -- remove DOS line endings
475 tabFilter _ ('\r':'\n':xs) = '\n':(tabFilter tabStop xs)
476 tabFilter _ ('\r':xs) = '\n':(tabFilter tabStop xs)
477 tabFilter spsToNextStop ('\t':xs) =
478 if preserveTabs
479 then '\t':(tabFilter tabStop xs)
480 else replicate spsToNextStop ' ' ++ tabFilter tabStop xs
481 tabFilter 1 (x:xs) =
482 x:(tabFilter tabStop xs)
483 tabFilter spsToNextStop (x:xs) =
484 x:(tabFilter (spsToNextStop - 1) xs)
486 let startParserState =
487 defaultParserState { stateParseRaw = parseRaw,
488 stateTabStop = tabStop,
489 stateSanitizeHTML = sanitize,
490 stateStandalone = standalone && (not strict),
491 stateSmart = smart || writerName' `elem`
492 ["latex", "context"],
493 stateColumns = columns,
494 stateStrict = strict }
495 let csslink = if (css == "")
496 then ""
497 else "<link rel=\"stylesheet\" href=\"" ++ css ++
498 "\" type=\"text/css\" media=\"all\" />\n"
499 let header = (if (customHeader == "DEFAULT")
500 then defaultHeader
501 else customHeader) ++ csslink ++ includeHeader
502 let writerOptions = WriterOptions { writerStandalone = standalone &&
503 (not strict),
504 writerHeader = header,
505 writerTitlePrefix = titlePrefix,
506 writerTabStop = tabStop,
507 writerTableOfContents = toc &&
508 (not strict) &&
509 writerName/="s5",
510 writerHTMLMathMethod = mathMethod,
511 writerS5 = (writerName=="s5"),
512 writerIgnoreNotes = False,
513 writerIncremental = incremental,
514 writerNumberSections = numberSections,
515 writerIncludeBefore = includeBefore,
516 writerIncludeAfter = includeAfter,
517 writerStrictMarkdown = strict,
518 writerReferenceLinks = referenceLinks,
519 writerWrapText = wrap }
521 (readSources sources) >>= (hPutStrLn output . toUTF8 .
522 (writer writerOptions) .
523 (reader startParserState) . tabFilter tabStop .
524 fromUTF8 . (joinWithSep "\n")) >>
525 hClose output
527 where
528 readSources [] = mapM readSource ["-"]
529 readSources sources = mapM readSource sources
530 readSource "-" = getContents
531 readSource source = readFile source