git.iko.soy/typeable/pandoc
on 83cddbc help sign in
442 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.LaTeX ( writeLaTeX )
41import Text.Pandoc.Readers.LaTeX ( readLaTeX )
42import Text.Pandoc.Writers.RTF ( writeRTF )
43import Text.Pandoc.Writers.Markdown ( writeMarkdown )
44import Text.Pandoc.Writers.DefaultHeaders ( defaultHtmlHeader,
45 defaultRTFHeader, defaultS5Header, defaultLaTeXHeader )
46import Text.Pandoc.Definition
47import Text.Pandoc.Shared
48import Text.Regex ( mkRegex, matchRegex )
49import System ( exitWith, getArgs, getProgName )
50import System.Exit
51import System.Console.GetOpt
52import System.IO
53import Data.Maybe ( fromMaybe )
54import Data.List ( isPrefixOf )
55import Char ( toLower )
56import Control.Monad ( (>>=) )
58version :: String
59version = "0.3"
61copyrightMessage :: String
62copyrightMessage = "\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."
64-- | Association list of formats and readers.
65readers :: [(String, ParserState -> String -> Pandoc)]
66readers = [("native" , readPandoc)
67 ,("markdown" , readMarkdown)
68 ,("rst" , readRST)
69 ,("html" , readHtml)
70 ,("latex" , readLaTeX)
71 ]
73-- | Reader for native Pandoc format.
74readPandoc :: ParserState -> String -> Pandoc
75readPandoc state input = read input
77-- | Association list of formats and pairs of writers and default headers.
78writers :: [ ( String, ( WriterOptions -> Pandoc -> String, String ) ) ]
79writers = [("native" , (writeDoc, ""))
80 ,("html" , (writeHtml, defaultHtmlHeader))
81 ,("s5" , (writeS5, defaultS5Header))
82 ,("latex" , (writeLaTeX, defaultLaTeXHeader))
83 ,("markdown" , (writeMarkdown, ""))
84 ,("rst" , (writeRST, ""))
85 ,("rtf" , (writeRTF, defaultRTFHeader))
86 ]
88-- | Writer for Pandoc native format.
89writeDoc :: WriterOptions -> Pandoc -> String
90writeDoc options = prettyPandoc
92-- | Data structure for command line options.
93data Opt = Opt
94 { optPreserveTabs :: Bool -- ^ Convert tabs to spaces
95 , optTabStop :: Int -- ^ Number of spaces per tab
96 , optStandalone :: Bool -- ^ Include header, footer
97 , optReader :: String -- ^ Reader format
98 , optWriter :: String -- ^ Writer format
99 , optParseRaw :: Bool -- ^ Parse unconvertable HTML and TeX
100 , optCSS :: String -- ^ CSS file to link to
101 , optIncludeInHeader :: String -- ^ File to include in header
102 , optIncludeBeforeBody :: String -- ^ File to include at top of body
103 , optIncludeAfterBody :: String -- ^ File to include at end of body
104 , optCustomHeader :: String -- ^ Custom header to use, or "DEFAULT"
105 , optTitlePrefix :: String -- ^ Optional prefix for HTML title
106 , optOutputFile :: String -- ^ Name of output file
107 , optNumberSections :: Bool -- ^ Number sections in LaTeX
108 , optIncremental :: Bool -- ^ Use incremental lists in S5
109 , optSmart :: Bool -- ^ Use smart typography
110 , optASCIIMathML :: Bool -- ^ Use ASCIIMathML in HTML
111 , optDebug :: Bool -- ^ Output debug messages
112 , optStrict :: Bool -- ^ Use strict markdown syntax
113 }
115-- | Defaults for command-line options.
116defaultOpts :: Opt
117defaultOpts = Opt
118 { optPreserveTabs = False
119 , optTabStop = 4
120 , optStandalone = False
121 , optReader = "" -- null for default reader
122 , optWriter = "" -- null for default writer
123 , optParseRaw = False
124 , optCSS = ""
125 , optIncludeInHeader = ""
126 , optIncludeBeforeBody = ""
127 , optIncludeAfterBody = ""
128 , optCustomHeader = "DEFAULT"
129 , optTitlePrefix = ""
130 , optOutputFile = "" -- null for stdout
131 , optNumberSections = False
132 , optIncremental = False
133 , optSmart = False
134 , optASCIIMathML = False
135 , optDebug = False
136 , optStrict = 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 "" -- "Use strict markdown syntax with no extensions"
182 , Option "R" ["parse-raw"]
183 (NoArg
184 (\opt -> return opt { optParseRaw = True }))
185 "" -- "Parse untranslatable HTML codes and LaTeX environments as raw"
187 , Option "S" ["smart"]
188 (NoArg
189 (\opt -> return opt { optSmart = True }))
190 "" -- "Use smart quotes, dashes, and ellipses in HTML output"
192 , Option "m" ["asciimathml"]
193 (NoArg
194 (\opt -> return opt { optASCIIMathML = True,
195 optStandalone = True }))
196 "" -- "Use ASCIIMathML script in html output"
198 , Option "i" ["incremental"]
199 (NoArg
200 (\opt -> return opt { optIncremental = True }))
201 "" -- "Make list items display incrementally in S5"
203 , Option "N" ["number-sections"]
204 (NoArg
205 (\opt -> return opt { optNumberSections = True }))
206 "" -- "Number sections in LaTeX"
208 , Option "c" ["css"]
209 (ReqArg
210 (\arg opt -> return opt { optCSS = arg,
211 optStandalone = True })
212 "CSS")
213 "" -- "Link to CSS style sheet"
215 , Option "H" ["include-in-header"]
216 (ReqArg
217 (\arg opt -> do
218 text <- readFile arg
219 return opt { optIncludeInHeader = text,
220 optStandalone = True })
221 "FILENAME")
222 "" -- "File to include at end of header (implies -s)"
224 , Option "B" ["include-before-body"]
225 (ReqArg
226 (\arg opt -> do
227 text <- readFile arg
228 return opt { optIncludeBeforeBody = text })
229 "FILENAME")
230 "" -- "File to include before document body"
232 , Option "A" ["include-after-body"]
233 (ReqArg
234 (\arg opt -> do
235 text <- readFile arg
236 return opt { optIncludeAfterBody = text })
237 "FILENAME")
238 "" -- "File to include after document body"
240 , Option "C" ["custom-header"]
241 (ReqArg
242 (\arg opt -> do
243 text <- readFile arg
244 return opt { optCustomHeader = text,
245 optStandalone = True })
246 "FILENAME")
247 "" -- "File to use for custom header (implies -s)"
249 , Option "T" ["title-prefix"]
250 (ReqArg
251 (\arg opt -> return opt { optTitlePrefix = arg,
252 optStandalone = True })
253 "STRING")
254 "" -- "String to prefix to HTML window title"
256 , Option "D" ["print-default-header"]
257 (ReqArg
258 (\arg opt -> do
259 let header = case (lookup arg writers) of
260 Just (writer, head) -> head
261 Nothing -> error ("Unknown reader: " ++ arg)
262 hPutStr stdout header
263 exitWith ExitSuccess)
264 "FORMAT")
265 "" -- "Print default header for FORMAT"
267 , Option "d" ["debug"]
268 (NoArg
269 (\opt -> return opt { optDebug = True }))
270 "" -- "Print debug messages to stderr, output to stdout"
272 , Option "v" ["version"]
273 (NoArg
274 (\_ -> do
275 prg <- getProgName
276 hPutStrLn stderr (prg ++ " " ++ version ++
277 copyrightMessage)
278 exitWith $ ExitFailure 2))
279 "" -- "Print version"
281 , Option "h" ["help"]
282 (NoArg
283 (\_ -> do
284 prg <- getProgName
285 hPutStr stderr (usageMessage prg options)
286 exitWith $ ExitFailure 2))
287 "" -- "Show help"
288 ]
290-- Returns usage message
291usageMessage :: String -> [OptDescr (Opt -> IO Opt)] -> String
292usageMessage programName options = usageInfo
293 (programName ++ " [OPTIONS] [FILES]" ++ "\nInput formats: " ++
294 joinWithSep ", " (map fst readers) ++ "\nOutput formats: " ++
295 joinWithSep ", " (map fst writers) ++ "\nOptions:")
296 options
298-- Determine default reader based on source file extensions
299defaultReaderName :: [String] -> String
300defaultReaderName [] = "markdown"
301defaultReaderName (x:xs) =
302 let x' = map toLower x in
303 case (matchRegex (mkRegex ".*\\.(.*)") x') of
304 Nothing -> defaultReaderName xs -- no extension
305 Just ["xhtml"] -> "html"
306 Just ["html"] -> "html"
307 Just ["htm"] -> "html"
308 Just ["tex"] -> "latex"
309 Just ["latex"] -> "latex"
310 Just ["ltx"] -> "latex"
311 Just ["rst"] -> "rst"
312 Just ["native"] -> "native"
313 Just _ -> "markdown"
315-- Determine default writer based on output file extension
316defaultWriterName :: String -> String
317defaultWriterName "" = "html" -- no output file
318defaultWriterName x =
319 let x' = map toLower x in
320 case (matchRegex (mkRegex ".*\\.(.*)") x') of
321 Nothing -> "markdown" -- no extension
322 Just [""] -> "markdown" -- empty extension
323 Just ["tex"] -> "latex"
324 Just ["latex"] -> "latex"
325 Just ["ltx"] -> "latex"
326 Just ["rtf"] -> "rtf"
327 Just ["rst"] -> "rst"
328 Just ["s5"] -> "s5"
329 Just ["native"] -> "native"
330 Just ["txt"] -> "markdown"
331 Just ["text"] -> "markdown"
332 Just ["md"] -> "markdown"
333 Just ["markdown"] -> "markdown"
334 Just _ -> "html"
336main = do
338 args <- getArgs
339 let (actions, sources, errors) = getOpt Permute options args
341 if (not (null errors))
342 then do
343 name <- getProgName
344 mapM (\e -> hPutStrLn stderr e) errors
345 hPutStr stderr (usageMessage name options)
346 exitWith $ ExitFailure 2
347 else
348 return ()
350 -- thread option data structure through all supplied option actions
351 opts <- foldl (>>=) (return defaultOpts) actions
353 let Opt { optPreserveTabs = preserveTabs
354 , optTabStop = tabStop
355 , optStandalone = standalone
356 , optReader = readerName
357 , optWriter = writerName
358 , optParseRaw = parseRaw
359 , optCSS = css
360 , optIncludeInHeader = includeHeader
361 , optIncludeBeforeBody = includeBefore
362 , optIncludeAfterBody = includeAfter
363 , optCustomHeader = customHeader
364 , optTitlePrefix = titlePrefix
365 , optOutputFile = outputFile
366 , optNumberSections = numberSections
367 , optIncremental = incremental
368 , optSmart = smart
369 , optASCIIMathML = asciiMathML
370 , optDebug = debug
371 , optStrict = strict
372 } = opts
374 -- assign reader and writer based on options and filenames
375 let readerName' = if null readerName
376 then defaultReaderName sources
377 else readerName
379 let writerName' = if null writerName
380 then defaultWriterName outputFile
381 else writerName
383 reader <- case (lookup readerName' readers) of
384 Just r -> return r
385 Nothing -> error ("Unknown reader: " ++ readerName')
387 (writer, defaultHeader) <- case (lookup writerName' writers) of
388 Just (w,h) -> return (w, h)
389 Nothing -> error ("Unknown writer: " ++ writerName')
391 output <- if ((null outputFile) || debug)
392 then return stdout
393 else openFile outputFile WriteMode
395 if debug
396 then do
397 hPutStrLn stderr ("OUTPUT=" ++ outputFile)
398 hPutStr stderr $ concatMap (\s -> "INPUT=" ++ s ++ "\n") sources
399 else return ()
401 let tabFilter = if preserveTabs then id else (tabsToSpaces tabStop)
402 let addBlank str = str ++ "\n\n"
403 let removeCRs str = filter (/= '\r') str -- remove DOS-style line endings
404 let filter = tabFilter . addBlank . removeCRs
405 let startParserState = defaultParserState { stateParseRaw = parseRaw,
406 stateTabStop = tabStop,
407 stateStandalone = standalone &&
408 (not strict),
409 stateStrict = strict }
410 let csslink = if (css == "")
411 then ""
412 else "<link rel=\"stylesheet\" href=\"" ++ css ++
413 "\" type=\"text/css\" media=\"all\" />\n"
414 let asciiMathMLLink = if asciiMathML then asciiMathMLScript else ""
415 let header = (if (customHeader == "DEFAULT")
416 then defaultHeader
417 else customHeader) ++
418 csslink ++ asciiMathMLLink ++ includeHeader
419 let writerOptions = WriterOptions { writerStandalone = standalone &&
420 (not strict),
421 writerHeader = header,
422 writerTitlePrefix = titlePrefix,
423 writerSmart = smart &&
424 (not strict),
425 writerTabStop = tabStop,
426 writerS5 = (writerName=="s5"),
427 writerIncremental = incremental,
428 writerNumberSections = numberSections,
429 writerIncludeBefore = includeBefore,
430 writerIncludeAfter = includeAfter,
431 writerStrictMarkdown = strict }
433 (readSources sources) >>= (hPutStr output . encodeUTF8 .
434 (writer writerOptions) .
435 (reader startParserState) . filter .
436 decodeUTF8 . (joinWithSep "\n")) >> hClose output
438 where
439 readSources [] = mapM readSource ["-"]
440 readSources sources = mapM readSource sources
441 readSource "-" = getContents
442 readSource source = readFile source