git.iko.soy/typeable/pandoc
on 339fcbb help sign in
449 lines 18 kB history raw
1{-
2Copyright (C) 2006 John MacFarlane <jgm at berkeley dot 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 John MacFarlane
22 License : GNU GPL, version 2 or above
24 Maintainer : John MacFarlane <jgm at berkeley dot 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 ( s5CSS, s5Javascript, writeS5 )
36import Text.Pandoc.Writers.RST ( writeRST )
37import Text.Pandoc.Readers.RST ( readRST )
38import Text.Pandoc.ASCIIMathML ( asciiMathMLScript )
39import Text.Pandoc.Writers.HTML ( writeHtml )
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.Markdown ( writeMarkdown )
45import Text.Pandoc.Writers.DefaultHeaders ( defaultHtmlHeader,
46 defaultRTFHeader, defaultS5Header, defaultLaTeXHeader,
47 defaultDocbookHeader )
48import Text.Pandoc.Definition
49import Text.Pandoc.Shared
50import Text.Regex ( mkRegex, matchRegex )
51import System ( exitWith, getArgs, getProgName )
52import System.Exit
53import System.Console.GetOpt
54import System.IO
55import Data.Maybe ( fromMaybe )
56import Data.List ( isPrefixOf )
57import Char ( toLower )
58import Control.Monad ( (>>=) )
60version :: String
61version = "0.3"
63copyrightMessage :: String
64copyrightMessage = "\nCopyright (C) 2006 John MacFarlane\nWeb: http://sophos.berkeley.edu/macfarlane/pandoc\nThis is free software; see the source for copying conditions. There is no\nwarranty, not even for merchantability or fitness for a particular purpose."
66-- | Association list of formats and readers.
67readers :: [(String, ParserState -> String -> Pandoc)]
68readers = [("native" , readPandoc)
69 ,("markdown" , readMarkdown)
70 ,("rst" , readRST)
71 ,("html" , readHtml)
72 ,("latex" , readLaTeX)
73 ]
75-- | Reader for native Pandoc format.
76readPandoc :: ParserState -> String -> Pandoc
77readPandoc state input = read input
79-- | Association list of formats and pairs of writers and default headers.
80writers :: [ ( String, ( WriterOptions -> Pandoc -> String, String ) ) ]
81writers = [("native" , (writeDoc, ""))
82 ,("html" , (writeHtml, defaultHtmlHeader))
83 ,("s5" , (writeS5, defaultS5Header))
84 ,("docbook" , (writeDocbook, defaultDocbookHeader))
85 ,("latex" , (writeLaTeX, defaultLaTeXHeader))
86 ,("markdown" , (writeMarkdown, ""))
87 ,("rst" , (writeRST, ""))
88 ,("rtf" , (writeRTF, defaultRTFHeader))
89 ]
91-- | Writer for Pandoc native format.
92writeDoc :: WriterOptions -> Pandoc -> String
93writeDoc options = prettyPandoc
95-- | Data structure for command line options.
96data Opt = Opt
97 { optPreserveTabs :: Bool -- ^ Convert tabs to spaces
98 , optTabStop :: Int -- ^ Number of spaces per tab
99 , optStandalone :: Bool -- ^ Include header, footer
100 , optReader :: String -- ^ Reader format
101 , optWriter :: String -- ^ Writer format
102 , optParseRaw :: Bool -- ^ Parse unconvertable HTML and TeX
103 , optCSS :: String -- ^ CSS file to link to
104 , optIncludeInHeader :: String -- ^ File to include in header
105 , optIncludeBeforeBody :: String -- ^ File to include at top of body
106 , optIncludeAfterBody :: String -- ^ File to include at end of body
107 , optCustomHeader :: String -- ^ Custom header to use, or "DEFAULT"
108 , optTitlePrefix :: String -- ^ Optional prefix for HTML title
109 , optOutputFile :: String -- ^ Name of output file
110 , optNumberSections :: Bool -- ^ Number sections in LaTeX
111 , optIncremental :: Bool -- ^ Use incremental lists in S5
112 , optSmart :: Bool -- ^ Use smart typography
113 , optASCIIMathML :: Bool -- ^ Use ASCIIMathML in HTML
114 , optDebug :: Bool -- ^ Output debug messages
115 , optStrict :: Bool -- ^ Use strict markdown syntax
116 }
118-- | Defaults for command-line options.
119defaultOpts :: Opt
120defaultOpts = Opt
121 { optPreserveTabs = False
122 , optTabStop = 4
123 , optStandalone = False
124 , optReader = "" -- null for default reader
125 , optWriter = "" -- null for default writer
126 , optParseRaw = False
127 , optCSS = ""
128 , optIncludeInHeader = ""
129 , optIncludeBeforeBody = ""
130 , optIncludeAfterBody = ""
131 , optCustomHeader = "DEFAULT"
132 , optTitlePrefix = ""
133 , optOutputFile = "" -- null for stdout
134 , optNumberSections = False
135 , optIncremental = False
136 , optSmart = False
137 , optASCIIMathML = False
138 , optDebug = False
139 , optStrict = False
140 }
142-- | A list of functions, each transforming the options data structure
143-- in response to a command-line option.
144options :: [OptDescr (Opt -> IO Opt)]
145options =
146 [ Option "fr" ["from","read"]
147 (ReqArg
148 (\arg opt -> return opt { optReader = map toLower arg })
149 "FORMAT")
150 "" -- ("(" ++ (joinWithSep ", " (map fst readers)) ++ ")")
152 , Option "tw" ["to","write"]
153 (ReqArg
154 (\arg opt -> return opt { optWriter = map toLower arg })
155 "FORMAT")
156 "" -- ("(" ++ (joinWithSep ", " (map fst writers)) ++ ")")
158 , Option "s" ["standalone"]
159 (NoArg
160 (\opt -> return opt { optStandalone = True }))
161 "" -- "Include needed header and footer on output"
163 , Option "o" ["output"]
164 (ReqArg
165 (\arg opt -> return opt { optOutputFile = arg })
166 "FILENAME")
167 "" -- "Name of output file"
169 , Option "p" ["preserve-tabs"]
170 (NoArg
171 (\opt -> return opt { optPreserveTabs = True }))
172 "" -- "Preserve tabs instead of converting to spaces"
174 , Option "" ["tab-stop"]
175 (ReqArg
176 (\arg opt -> return opt { optTabStop = (read arg) } )
177 "TABSTOP")
178 "" -- "Tab stop (default 4)"
180 , Option "" ["strict"]
181 (NoArg
182 (\opt -> return opt { optStrict = True } ))
183 "" -- "Use strict markdown syntax with no extensions"
185 , Option "R" ["parse-raw"]
186 (NoArg
187 (\opt -> return opt { optParseRaw = True }))
188 "" -- "Parse untranslatable HTML codes and LaTeX environments as raw"
190 , Option "S" ["smart"]
191 (NoArg
192 (\opt -> return opt { optSmart = True }))
193 "" -- "Use smart quotes, dashes, and ellipses in HTML output"
195 , Option "m" ["asciimathml"]
196 (NoArg
197 (\opt -> return opt { optASCIIMathML = True,
198 optStandalone = True }))
199 "" -- "Use ASCIIMathML script in html output"
201 , Option "i" ["incremental"]
202 (NoArg
203 (\opt -> return opt { optIncremental = True }))
204 "" -- "Make list items display incrementally in S5"
206 , Option "N" ["number-sections"]
207 (NoArg
208 (\opt -> return opt { optNumberSections = True }))
209 "" -- "Number sections in LaTeX"
211 , Option "c" ["css"]
212 (ReqArg
213 (\arg opt -> return opt { optCSS = arg,
214 optStandalone = True })
215 "CSS")
216 "" -- "Link to CSS style sheet"
218 , Option "H" ["include-in-header"]
219 (ReqArg
220 (\arg opt -> do
221 text <- readFile arg
222 return opt { optIncludeInHeader = text,
223 optStandalone = True })
224 "FILENAME")
225 "" -- "File to include at end of header (implies -s)"
227 , Option "B" ["include-before-body"]
228 (ReqArg
229 (\arg opt -> do
230 text <- readFile arg
231 return opt { optIncludeBeforeBody = text })
232 "FILENAME")
233 "" -- "File to include before document body"
235 , Option "A" ["include-after-body"]
236 (ReqArg
237 (\arg opt -> do
238 text <- readFile arg
239 return opt { optIncludeAfterBody = text })
240 "FILENAME")
241 "" -- "File to include after document body"
243 , Option "C" ["custom-header"]
244 (ReqArg
245 (\arg opt -> do
246 text <- readFile arg
247 return opt { optCustomHeader = text,
248 optStandalone = True })
249 "FILENAME")
250 "" -- "File to use for custom header (implies -s)"
252 , Option "T" ["title-prefix"]
253 (ReqArg
254 (\arg opt -> return opt { optTitlePrefix = arg,
255 optStandalone = True })
256 "STRING")
257 "" -- "String to prefix to HTML window title"
259 , Option "D" ["print-default-header"]
260 (ReqArg
261 (\arg opt -> do
262 let header = case (lookup arg writers) of
263 Just (writer, head) -> head
264 Nothing -> error ("Unknown reader: " ++ arg)
265 hPutStr stdout header
266 exitWith ExitSuccess)
267 "FORMAT")
268 "" -- "Print default header for FORMAT"
270 , Option "d" ["debug"]
271 (NoArg
272 (\opt -> return opt { optDebug = True }))
273 "" -- "Print debug messages to stderr, output to stdout"
275 , Option "v" ["version"]
276 (NoArg
277 (\_ -> do
278 prg <- getProgName
279 hPutStrLn stderr (prg ++ " " ++ version ++
280 copyrightMessage)
281 exitWith $ ExitFailure 2))
282 "" -- "Print version"
284 , Option "h" ["help"]
285 (NoArg
286 (\_ -> do
287 prg <- getProgName
288 hPutStr stderr (usageMessage prg options)
289 exitWith $ ExitFailure 2))
290 "" -- "Show help"
291 ]
293-- Returns usage message
294usageMessage :: String -> [OptDescr (Opt -> IO Opt)] -> String
295usageMessage programName options = usageInfo
296 (programName ++ " [OPTIONS] [FILES]" ++ "\nInput formats: " ++
297 joinWithSep ", " (map fst readers) ++ "\nOutput formats: " ++
298 joinWithSep ", " (map fst writers) ++ "\nOptions:")
299 options
301-- Determine default reader based on source file extensions
302defaultReaderName :: [String] -> String
303defaultReaderName [] = "markdown"
304defaultReaderName (x:xs) =
305 let x' = map toLower x in
306 case (matchRegex (mkRegex ".*\\.(.*)") x') of
307 Nothing -> defaultReaderName xs -- no extension
308 Just ["xhtml"] -> "html"
309 Just ["html"] -> "html"
310 Just ["htm"] -> "html"
311 Just ["tex"] -> "latex"
312 Just ["latex"] -> "latex"
313 Just ["ltx"] -> "latex"
314 Just ["rst"] -> "rst"
315 Just ["native"] -> "native"
316 Just _ -> "markdown"
318-- Determine default writer based on output file extension
319defaultWriterName :: String -> String
320defaultWriterName "" = "html" -- no output file
321defaultWriterName x =
322 let x' = map toLower x in
323 case (matchRegex (mkRegex ".*\\.(.*)") x') of
324 Nothing -> "markdown" -- no extension
325 Just [""] -> "markdown" -- empty extension
326 Just ["tex"] -> "latex"
327 Just ["latex"] -> "latex"
328 Just ["ltx"] -> "latex"
329 Just ["rtf"] -> "rtf"
330 Just ["rst"] -> "rst"
331 Just ["s5"] -> "s5"
332 Just ["native"] -> "native"
333 Just ["txt"] -> "markdown"
334 Just ["text"] -> "markdown"
335 Just ["md"] -> "markdown"
336 Just ["markdown"] -> "markdown"
337 Just ["db"] -> "docbook"
338 Just ["xml"] -> "docbook"
339 Just ["sgml"] -> "docbook"
340 Just _ -> "html"
342main = do
344 args <- getArgs
345 let (actions, sources, errors) = getOpt Permute options args
347 if (not (null errors))
348 then do
349 name <- getProgName
350 mapM (\e -> hPutStrLn stderr e) errors
351 hPutStr stderr (usageMessage name options)
352 exitWith $ ExitFailure 2
353 else
354 return ()
356 -- thread option data structure through all supplied option actions
357 opts <- foldl (>>=) (return defaultOpts) actions
359 let Opt { optPreserveTabs = preserveTabs
360 , optTabStop = tabStop
361 , optStandalone = standalone
362 , optReader = readerName
363 , optWriter = writerName
364 , optParseRaw = parseRaw
365 , optCSS = css
366 , optIncludeInHeader = includeHeader
367 , optIncludeBeforeBody = includeBefore
368 , optIncludeAfterBody = includeAfter
369 , optCustomHeader = customHeader
370 , optTitlePrefix = titlePrefix
371 , optOutputFile = outputFile
372 , optNumberSections = numberSections
373 , optIncremental = incremental
374 , optSmart = smart
375 , optASCIIMathML = asciiMathML
376 , optDebug = debug
377 , optStrict = strict
378 } = opts
380 -- assign reader and writer based on options and filenames
381 let readerName' = if null readerName
382 then defaultReaderName sources
383 else readerName
385 let writerName' = if null writerName
386 then defaultWriterName outputFile
387 else writerName
389 reader <- case (lookup readerName' readers) of
390 Just r -> return r
391 Nothing -> error ("Unknown reader: " ++ readerName')
393 (writer, defaultHeader) <- case (lookup writerName' writers) of
394 Just (w,h) -> return (w, h)
395 Nothing -> error ("Unknown writer: " ++ writerName')
397 output <- if ((null outputFile) || debug)
398 then return stdout
399 else openFile outputFile WriteMode
401 if debug
402 then do
403 hPutStrLn stderr ("OUTPUT=" ++ outputFile)
404 hPutStr stderr $ concatMap (\s -> "INPUT=" ++ s ++ "\n") sources
405 else return ()
407 let tabFilter = if preserveTabs then id else (tabsToSpaces tabStop)
408 let addBlank str = str ++ "\n\n"
409 let removeCRs str = filter (/= '\r') str -- remove DOS-style line endings
410 let filter = tabFilter . addBlank . removeCRs
411 let startParserState = defaultParserState { stateParseRaw = parseRaw,
412 stateTabStop = tabStop,
413 stateStandalone = standalone &&
414 (not strict),
415 stateStrict = strict }
416 let csslink = if (css == "")
417 then ""
418 else "<link rel=\"stylesheet\" href=\"" ++ css ++
419 "\" type=\"text/css\" media=\"all\" />\n"
420 let asciiMathMLLink = if asciiMathML then asciiMathMLScript else ""
421 let header = (if (customHeader == "DEFAULT")
422 then defaultHeader
423 else customHeader) ++
424 csslink ++ asciiMathMLLink ++ includeHeader
425 let writerOptions = WriterOptions { writerStandalone = standalone &&
426 (not strict),
427 writerHeader = header,
428 writerTitlePrefix = titlePrefix,
429 writerSmart = smart &&
430 (not strict),
431 writerTabStop = tabStop,
432 writerNotes = [],
433 writerS5 = (writerName=="s5"),
434 writerIncremental = incremental,
435 writerNumberSections = numberSections,
436 writerIncludeBefore = includeBefore,
437 writerIncludeAfter = includeAfter,
438 writerStrictMarkdown = strict }
440 (readSources sources) >>= (hPutStr output . encodeUTF8 .
441 (writer writerOptions) .
442 (reader startParserState) . filter .
443 decodeUTF8 . (joinWithSep "\n")) >> hClose output
445 where
446 readSources [] = mapM readSource ["-"]
447 readSources sources = mapM readSource sources
448 readSource "-" = getContents
449 readSource source = readFile source