git.iko.soy/typeable/pandoc
on 656dd64 help sign in
503 lines 20 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.UTF8 ( decodeUTF8, encodeUTF8 )
33import Text.Pandoc.Readers.Markdown ( readMarkdown )
34import Text.Pandoc.Readers.HTML ( readHtml )
35import Text.Pandoc.Writers.S5 ( writeS5String )
36import Text.Pandoc.Writers.RST ( writeRST )
37import Text.Pandoc.Readers.RST ( readRST )
38import Text.Pandoc.ASCIIMathML ( asciiMathMLScript )
39import Text.Pandoc.Writers.HTML ( writeHtmlString )
40import Text.Pandoc.Writers.Docbook ( writeDocbook )
41import Text.Pandoc.Writers.LaTeX ( writeLaTeX )
42import Text.Pandoc.Readers.LaTeX ( readLaTeX )
43import Text.Pandoc.Writers.RTF ( writeRTF )
44import Text.Pandoc.Writers.Man ( writeMan )
45import Text.Pandoc.Writers.Markdown ( writeMarkdown )
46import Text.Pandoc.Writers.DefaultHeaders ( defaultRTFHeader,
47 defaultS5Header,
48 defaultLaTeXHeader,
49 defaultDocbookHeader )
50import Text.Pandoc.Definition
51import Text.Pandoc.Shared
52import Text.Regex ( mkRegex, matchRegex )
53import System.Environment ( getArgs, getProgName, getEnvironment )
54import System.Exit ( exitWith, ExitCode (..) )
55import System.Console.GetOpt
56import System.IO
57import Data.Maybe ( fromMaybe )
58import Data.List ( isPrefixOf )
59import Data.Char ( toLower )
60import Control.Monad ( (>>=) )
62version :: String
63version = "0.4"
65copyrightMessage :: String
66copyrightMessage = "\nCopyright (C) 2006-7 John MacFarlane\n\
67 \Web: http://sophos.berkeley.edu/macfarlane/pandoc\n\
68 \This is free software; see the source for copying conditions. There is no\n\
69 \warranty, not even for merchantability or fitness for a particular purpose."
71-- | Association list of formats and readers.
72readers :: [(String, ParserState -> String -> Pandoc)]
73readers = [("native" , readPandoc)
74 ,("markdown" , readMarkdown)
75 ,("rst" , readRST)
76 ,("html" , readHtml)
77 ,("latex" , readLaTeX)
78 ]
80-- | Reader for native Pandoc format.
81readPandoc :: ParserState -> String -> Pandoc
82readPandoc state input = read input
84-- | Association list of formats and pairs of writers and default headers.
85writers :: [ ( String, ( WriterOptions -> Pandoc -> String, String ) ) ]
86writers = [("native" , (writeDoc, ""))
87 ,("html" , (writeHtmlString, ""))
88 ,("s5" , (writeS5String, defaultS5Header))
89 ,("docbook" , (writeDocbook, defaultDocbookHeader))
90 ,("latex" , (writeLaTeX, defaultLaTeXHeader))
91 ,("man" , (writeMan, ""))
92 ,("markdown" , (writeMarkdown, ""))
93 ,("rst" , (writeRST, ""))
94 ,("rtf" , (writeRTF, defaultRTFHeader))
95 ]
97-- | Writer for Pandoc native format.
98writeDoc :: WriterOptions -> Pandoc -> String
99writeDoc options = prettyPandoc
101-- | Data structure for command line options.
102data Opt = Opt
103 { optPreserveTabs :: Bool -- ^ Convert tabs to spaces
104 , optTabStop :: Int -- ^ Number of spaces per tab
105 , optStandalone :: Bool -- ^ Include header, footer
106 , optReader :: String -- ^ Reader format
107 , optWriter :: String -- ^ Writer format
108 , optParseRaw :: Bool -- ^ Parse unconvertable HTML and TeX
109 , optCSS :: String -- ^ CSS file to link to
110 , optTableOfContents :: Bool -- ^ Include table of contents
111 , optIncludeInHeader :: String -- ^ File to include in header
112 , optIncludeBeforeBody :: String -- ^ File to include at top of body
113 , optIncludeAfterBody :: String -- ^ File to include at end of body
114 , optCustomHeader :: String -- ^ Custom header to use, or "DEFAULT"
115 , optTitlePrefix :: String -- ^ Optional prefix for HTML title
116 , optOutputFile :: String -- ^ Name of output file
117 , optNumberSections :: Bool -- ^ Number sections in LaTeX
118 , optIncremental :: Bool -- ^ Use incremental lists in S5
119 , optSmart :: Bool -- ^ Use smart typography
120 , optASCIIMathML :: Bool -- ^ Use ASCIIMathML in HTML
121 , optDumpArgs :: Bool -- ^ Output command-line arguments
122 , optIgnoreArgs :: Bool -- ^ Ignore command-line arguments
123 , optStrict :: Bool -- ^ Use strict markdown syntax
124 , optReferenceLinks :: Bool -- ^ Use reference links in writing markdown, rst
125 }
127-- | Defaults for command-line options.
128defaultOpts :: Opt
129defaultOpts = Opt
130 { optPreserveTabs = False
131 , optTabStop = 4
132 , optStandalone = False
133 , optReader = "" -- null for default reader
134 , optWriter = "" -- null for default writer
135 , optParseRaw = False
136 , optCSS = ""
137 , optTableOfContents = False
138 , optIncludeInHeader = ""
139 , optIncludeBeforeBody = ""
140 , optIncludeAfterBody = ""
141 , optCustomHeader = "DEFAULT"
142 , optTitlePrefix = ""
143 , optOutputFile = "-" -- "-" means stdout
144 , optNumberSections = False
145 , optIncremental = False
146 , optSmart = False
147 , optASCIIMathML = False
148 , optDumpArgs = False
149 , optIgnoreArgs = False
150 , optStrict = False
151 , optReferenceLinks = False
152 }
154-- | A list of functions, each transforming the options data structure
155-- in response to a command-line option.
156options :: [OptDescr (Opt -> IO Opt)]
157options =
158 [ Option "fr" ["from","read"]
159 (ReqArg
160 (\arg opt -> return opt { optReader = map toLower arg })
161 "FORMAT")
162 "" -- ("(" ++ (joinWithSep ", " (map fst readers)) ++ ")")
164 , Option "tw" ["to","write"]
165 (ReqArg
166 (\arg opt -> return opt { optWriter = map toLower arg })
167 "FORMAT")
168 "" -- ("(" ++ (joinWithSep ", " (map fst writers)) ++ ")")
170 , Option "s" ["standalone"]
171 (NoArg
172 (\opt -> return opt { optStandalone = True }))
173 "" -- "Include needed header and footer on output"
175 , Option "o" ["output"]
176 (ReqArg
177 (\arg opt -> return opt { optOutputFile = arg })
178 "FILENAME")
179 "" -- "Name of output file"
181 , Option "p" ["preserve-tabs"]
182 (NoArg
183 (\opt -> return opt { optPreserveTabs = True }))
184 "" -- "Preserve tabs instead of converting to spaces"
186 , Option "" ["tab-stop"]
187 (ReqArg
188 (\arg opt -> return opt { optTabStop = (read arg) } )
189 "TABSTOP")
190 "" -- "Tab stop (default 4)"
192 , Option "" ["strict"]
193 (NoArg
194 (\opt -> return opt { optStrict = True } ))
195 "" -- "Use strict markdown syntax with no extensions"
197 , Option "" ["reference-links"]
198 (NoArg
199 (\opt -> return opt { optReferenceLinks = True } ))
200 "" -- "Use reference links in parsing HTML"
202 , Option "R" ["parse-raw"]
203 (NoArg
204 (\opt -> return opt { optParseRaw = True }))
205 "" -- "Parse untranslatable HTML codes and LaTeX environments as raw"
207 , Option "S" ["smart"]
208 (NoArg
209 (\opt -> return opt { optSmart = True }))
210 "" -- "Use smart quotes, dashes, and ellipses"
212 , Option "m" ["asciimathml"]
213 (NoArg
214 (\opt -> return opt { optASCIIMathML = True,
215 optStandalone = True }))
216 "" -- "Use ASCIIMathML script in html output"
218 , Option "i" ["incremental"]
219 (NoArg
220 (\opt -> return opt { optIncremental = True }))
221 "" -- "Make list items display incrementally in S5"
223 , Option "N" ["number-sections"]
224 (NoArg
225 (\opt -> return opt { optNumberSections = True }))
226 "" -- "Number sections in LaTeX"
228 , Option "" ["toc", "table-of-contents"]
229 (NoArg
230 (\opt -> return opt { optTableOfContents = True }))
231 "" -- "Include table of contents"
233 , Option "c" ["css"]
234 (ReqArg
235 (\arg opt -> return opt { optCSS = arg,
236 optStandalone = True })
237 "CSS")
238 "" -- "Link to CSS style sheet"
240 , Option "H" ["include-in-header"]
241 (ReqArg
242 (\arg opt -> do
243 text <- readFile arg
244 return opt { optIncludeInHeader = text,
245 optStandalone = True })
246 "FILENAME")
247 "" -- "File to include at end of header (implies -s)"
249 , Option "B" ["include-before-body"]
250 (ReqArg
251 (\arg opt -> do
252 text <- readFile arg
253 return opt { optIncludeBeforeBody = text })
254 "FILENAME")
255 "" -- "File to include before document body"
257 , Option "A" ["include-after-body"]
258 (ReqArg
259 (\arg opt -> do
260 text <- readFile arg
261 return opt { optIncludeAfterBody = text })
262 "FILENAME")
263 "" -- "File to include after document body"
265 , Option "C" ["custom-header"]
266 (ReqArg
267 (\arg opt -> do
268 text <- readFile arg
269 return opt { optCustomHeader = text,
270 optStandalone = True })
271 "FILENAME")
272 "" -- "File to use for custom header (implies -s)"
274 , Option "T" ["title-prefix"]
275 (ReqArg
276 (\arg opt -> return opt { optTitlePrefix = arg,
277 optStandalone = True })
278 "STRING")
279 "" -- "String to prefix to HTML window title"
281 , Option "D" ["print-default-header"]
282 (ReqArg
283 (\arg opt -> do
284 let header = case (lookup arg writers) of
285 Just (writer, head) -> head
286 Nothing -> error ("Unknown reader: " ++ arg)
287 hPutStr stdout header
288 exitWith ExitSuccess)
289 "FORMAT")
290 "" -- "Print default header for FORMAT"
292 , Option "" ["dump-args"]
293 (NoArg
294 (\opt -> return opt { optDumpArgs = True }))
295 "" -- "Print output filename and arguments to stdout."
297 , Option "" ["ignore-args"]
298 (NoArg
299 (\opt -> return opt { optIgnoreArgs = True }))
300 "" -- "Ignore command-line arguments."
302 , Option "v" ["version"]
303 (NoArg
304 (\_ -> do
305 prg <- getProgName
306 hPutStrLn stderr (prg ++ " " ++ version ++
307 copyrightMessage)
308 exitWith $ ExitFailure 4))
309 "" -- "Print version"
311 , Option "h" ["help"]
312 (NoArg
313 (\_ -> do
314 prg <- getProgName
315 hPutStr stderr (usageMessage prg options)
316 exitWith $ ExitFailure 2))
317 "" -- "Show help"
318 ]
320-- Returns usage message
321usageMessage :: String -> [OptDescr (Opt -> IO Opt)] -> String
322usageMessage programName options = usageInfo
323 (programName ++ " [OPTIONS] [FILES]" ++ "\nInput formats: " ++
324 joinWithSep ", " (map fst readers) ++ "\nOutput formats: " ++
325 joinWithSep ", " (map fst writers) ++ "\nOptions:")
326 options
328-- Determine default reader based on source file extensions
329defaultReaderName :: [String] -> String
330defaultReaderName [] = "markdown"
331defaultReaderName (x:xs) =
332 let x' = map toLower x in
333 case (matchRegex (mkRegex ".*\\.(.*)") x') of
334 Nothing -> defaultReaderName xs -- no extension
335 Just ["xhtml"] -> "html"
336 Just ["html"] -> "html"
337 Just ["htm"] -> "html"
338 Just ["tex"] -> "latex"
339 Just ["latex"] -> "latex"
340 Just ["ltx"] -> "latex"
341 Just ["rst"] -> "rst"
342 Just ["native"] -> "native"
343 Just _ -> "markdown"
345-- Determine default writer based on output file extension
346defaultWriterName :: String -> String
347defaultWriterName "-" = "html" -- no output file
348defaultWriterName x =
349 let x' = map toLower x in
350 case (matchRegex (mkRegex ".*\\.(.*)") x') of
351 Nothing -> "markdown" -- no extension
352 Just [""] -> "markdown" -- empty extension
353 Just ["tex"] -> "latex"
354 Just ["latex"] -> "latex"
355 Just ["ltx"] -> "latex"
356 Just ["rtf"] -> "rtf"
357 Just ["rst"] -> "rst"
358 Just ["s5"] -> "s5"
359 Just ["native"] -> "native"
360 Just ["txt"] -> "markdown"
361 Just ["text"] -> "markdown"
362 Just ["md"] -> "markdown"
363 Just ["markdown"] -> "markdown"
364 Just ["db"] -> "docbook"
365 Just ["xml"] -> "docbook"
366 Just ["sgml"] -> "docbook"
367 Just [[x]] | x `elem` ['1'..'9'] -> "man"
368 Just _ -> "html"
370main = do
372 rawArgs <- getArgs
373 prg <- getProgName
374 let compatMode = (prg == "hsmarkdown")
376 let (actions, args, errors) = if compatMode
377 then ([], rawArgs, [])
378 else getOpt Permute options rawArgs
380 if (not (null errors))
381 then do
382 name <- getProgName
383 mapM (\e -> hPutStrLn stderr e) errors
384 hPutStr stderr (usageMessage name options)
385 exitWith $ ExitFailure 2
386 else
387 return ()
389 let defaultOpts' = if compatMode
390 then defaultOpts { optReader = "markdown"
391 , optWriter = "html"
392 , optStrict = True }
393 else defaultOpts
395 -- thread option data structure through all supplied option actions
396 opts <- foldl (>>=) (return defaultOpts') actions
398 let Opt { optPreserveTabs = preserveTabs
399 , optTabStop = tabStop
400 , optStandalone = standalone
401 , optReader = readerName
402 , optWriter = writerName
403 , optParseRaw = parseRaw
404 , optCSS = css
405 , optTableOfContents = toc
406 , optIncludeInHeader = includeHeader
407 , optIncludeBeforeBody = includeBefore
408 , optIncludeAfterBody = includeAfter
409 , optCustomHeader = customHeader
410 , optTitlePrefix = titlePrefix
411 , optOutputFile = outputFile
412 , optNumberSections = numberSections
413 , optIncremental = incremental
414 , optSmart = smart
415 , optASCIIMathML = asciiMathML
416 , optDumpArgs = dumpArgs
417 , optIgnoreArgs = ignoreArgs
418 , optStrict = strict
419 , optReferenceLinks = referenceLinks
420 } = opts
422 if dumpArgs
423 then do
424 hPutStrLn stdout outputFile
425 mapM (\arg -> hPutStrLn stdout arg) args
426 exitWith $ ExitSuccess
427 else return ()
429 let sources = if ignoreArgs then [] else args
431 -- assign reader and writer based on options and filenames
432 let readerName' = if null readerName
433 then defaultReaderName sources
434 else readerName
436 let writerName' = if null writerName
437 then defaultWriterName outputFile
438 else writerName
440 reader <- case (lookup readerName' readers) of
441 Just r -> return r
442 Nothing -> error ("Unknown reader: " ++ readerName')
444 (writer, defaultHeader) <- case (lookup writerName' writers) of
445 Just (w,h) -> return (w, h)
446 Nothing -> error ("Unknown writer: " ++ writerName')
448 output <- if (outputFile == "-")
449 then return stdout
450 else openFile outputFile WriteMode
452 environment <- getEnvironment
453 let columns = case lookup "COLUMNS" environment of
454 Just cols -> read cols
455 Nothing -> stateColumns defaultParserState
457 let tabFilter = if preserveTabs then id else (tabsToSpaces tabStop)
458 let addBlank str = str
459 let removeCRs str = filter (/= '\r') str -- remove DOS-style line endings
460 let filter = tabFilter . addBlank . removeCRs
461 let startParserState =
462 defaultParserState { stateParseRaw = parseRaw,
463 stateTabStop = tabStop,
464 stateStandalone = standalone && (not strict),
465 stateSmart = smart || writerName' == "latex",
466 stateColumns = columns,
467 stateStrict = strict }
468 let csslink = if (css == "")
469 then ""
470 else "<link rel=\"stylesheet\" href=\"" ++ css ++
471 "\" type=\"text/css\" media=\"all\" />\n"
472 let asciiMathMLLink = if asciiMathML then asciiMathMLScript else ""
473 let header = (if (customHeader == "DEFAULT")
474 then defaultHeader
475 else customHeader) ++
476 csslink ++ asciiMathMLLink ++ includeHeader
477 let writerOptions = WriterOptions { writerStandalone = standalone &&
478 (not strict),
479 writerHeader = header,
480 writerTitlePrefix = titlePrefix,
481 writerTabStop = tabStop,
482 writerTableOfContents = toc &&
483 (not strict) &&
484 writerName/="s5",
485 writerS5 = (writerName=="s5"),
486 writerIgnoreNotes = False,
487 writerIncremental = incremental,
488 writerNumberSections = numberSections,
489 writerIncludeBefore = includeBefore,
490 writerIncludeAfter = includeAfter,
491 writerStrictMarkdown = strict,
492 writerReferenceLinks = referenceLinks }
494 (readSources sources) >>= (hPutStr output . encodeUTF8 .
495 (writer writerOptions) .
496 (reader startParserState) . filter .
497 decodeUTF8 . (joinWithSep "\n")) >> hClose output
499 where
500 readSources [] = mapM readSource ["-"]
501 readSources sources = mapM readSource sources
502 readSource "-" = getContents
503 readSource source = readFile source