git.iko.soy/typeable/pandoc
on cfaf0c1 help sign in
335 lines 14 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 System ( exitWith, getArgs, getProgName )
49import System.Exit
50import System.Console.GetOpt
51import System.IO
52import Data.Maybe ( fromMaybe )
53import Data.List ( isPrefixOf )
54import Char ( toLower )
55import Control.Monad ( (>>=) )
57version :: String
58version = "0.3"
60-- | Association list of formats and readers.
61readers :: [(String, ParserState -> String -> Pandoc)]
62readers = [("native" , readPandoc)
63 ,("markdown" , readMarkdown)
64 ,("rst" , readRST)
65 ,("html" , readHtml)
66 ,("latex" , readLaTeX)
67 ]
69-- | Reader for native Pandoc format.
70readPandoc :: ParserState -> String -> Pandoc
71readPandoc state input = read input
73-- | Association list of formats and pairs of writers and default headers.
74writers :: [ ( String, ( WriterOptions -> Pandoc -> String, String ) ) ]
75writers = [("native" , (writeDoc, ""))
76 ,("html" , (writeHtml, defaultHtmlHeader))
77 ,("s5" , (writeS5, defaultS5Header))
78 ,("latex" , (writeLaTeX, defaultLaTeXHeader))
79 ,("markdown" , (writeMarkdown, ""))
80 ,("rst" , (writeRST, ""))
81 ,("rtf" , (writeRTF, defaultRTFHeader))
82 ]
84-- | Writer for Pandoc native format.
85writeDoc :: WriterOptions -> Pandoc -> String
86writeDoc options = prettyPandoc
88-- | Data structure for command line options.
89data Opt = Opt
90 { optPreserveTabs :: Bool -- ^ If @False@, convert tabs to spaces
91 , optTabStop :: Int -- ^ Number of spaces per tab
92 , optStandalone :: Bool -- ^ If @True@, include header, footer
93 , optReader :: ParserState -> String -> Pandoc -- ^ Read format
94 , optWriter :: WriterOptions -> Pandoc -> String -- ^ Write fmt
95 , optParseRaw :: Bool -- ^ If @True@, parse unconvertable
96 -- HTML and TeX
97 , optCSS :: String -- ^ CSS file to link to
98 , optIncludeInHeader :: String -- ^ File to include in header
99 , optIncludeBeforeBody :: String -- ^ File to include at top of body
100 , optIncludeAfterBody :: String -- ^ File to include at end of body
101 , optCustomHeader :: String -- ^ Custom header to use, or "DEFAULT"
102 , optDefaultHeader :: String -- ^ Default header
103 , optTitlePrefix :: String -- ^ Optional prefix for HTML title
104 , optNumberSections :: Bool -- ^ If @True@, number sections in LaTeX
105 , optIncremental :: Bool -- ^ If @True@, incremental lists in S5
106 , optSmart :: Bool -- ^ If @True@, use smart typography
107 , optASCIIMathML :: Bool -- ^ If @True@, use ASCIIMathML in HTML
108 }
110-- | Defaults for command-line options.
111startOpt :: Opt
112startOpt = Opt
113 { optPreserveTabs = False
114 , optTabStop = 4
115 , optStandalone = False
116 , optReader = readMarkdown
117 , optWriter = writeHtml
118 , optParseRaw = False
119 , optCSS = ""
120 , optIncludeInHeader = ""
121 , optIncludeBeforeBody = ""
122 , optIncludeAfterBody = ""
123 , optCustomHeader = "DEFAULT"
124 , optDefaultHeader = defaultHtmlHeader
125 , optTitlePrefix = ""
126 , optNumberSections = False
127 , optIncremental = False
128 , optSmart = False
129 , optASCIIMathML = False
130 }
132-- | A list of functions, each transforming the options data structure in response
133-- to a command-line option.
134options :: [OptDescr (Opt -> IO Opt)]
135options =
136 [ Option "v" ["version"]
137 (NoArg
138 (\_ -> do
139 hPutStrLn stderr ("Version " ++ version)
140 exitWith ExitSuccess))
141 "Print version"
143 , Option "h" ["help"]
144 (NoArg
145 (\_ -> do
146 prg <- getProgName
147 hPutStrLn stderr (usageInfo (prg ++ " [OPTIONS] [FILES] - convert FILES from one markup format to another\nIf no OPTIONS specified, converts from markdown to html.\nIf no FILES specified, input is read from STDIN.\nOptions:") options)
148 exitWith ExitSuccess))
149 "Show help"
151 , Option "fr" ["from","read"]
152 (ReqArg
153 (\arg opt -> case (lookup (map toLower arg) readers) of
154 Just reader -> return opt { optReader = reader }
155 Nothing -> error ("Unknown reader: " ++ arg) )
156 "FORMAT")
157 ("Source format (" ++
158 (concatMap (\(name, fn) -> " " ++ name) readers) ++ " )")
160 , Option "tw" ["to","write"]
161 (ReqArg
162 (\arg opt -> case (lookup (map toLower arg) writers) of
163 Just (writer, defaultHeader) ->
164 return opt { optWriter = writer,
165 optDefaultHeader = defaultHeader }
166 Nothing -> error ("Unknown writer: " ++ arg) )
167 "FORMAT")
168 ("Output format (" ++ (concatMap (\(name, fn) -> " " ++ name) writers) ++ " )")
170 , Option "s" ["standalone"]
171 (NoArg
172 (\opt -> return opt { optStandalone = True }))
173 "Include needed header and footer on output"
175 , Option "p" ["preserve-tabs"]
176 (NoArg
177 (\opt -> return opt { optPreserveTabs = True }))
178 "Preserve tabs instead of converting to spaces"
180 , Option "" ["tab-stop"]
181 (ReqArg
182 (\arg opt -> return opt { optTabStop = (read arg) } )
183 "TABSTOP")
184 "Tab stop (default 4)"
186 , Option "R" ["parse-raw"]
187 (NoArg
188 (\opt -> return opt { optParseRaw = True }))
189 "Parse untranslatable HTML codes and LaTeX environments as raw"
191 , Option "S" ["smart"]
192 (NoArg
193 (\opt -> return opt { optSmart = True }))
194 "Use smart quotes, dashes, and ellipses in HTML output"
196 , Option "m" ["asciimathml"]
197 (NoArg
198 (\opt -> return opt { optASCIIMathML = True,
199 optStandalone = True }))
200 "Use ASCIIMathML script in html output"
202 , Option "i" ["incremental"]
203 (NoArg
204 (\opt -> return opt { optIncremental = True }))
205 "Make list items display incrementally in S5"
207 , Option "N" ["number-sections"]
208 (NoArg
209 (\opt -> return opt { optNumberSections = True }))
210 "Number sections in LaTeX"
212 , Option "c" ["css"]
213 (ReqArg
214 (\arg opt -> return opt { optCSS = arg,
215 optStandalone = True })
216 "CSS")
217 "Link to CSS style sheet"
219 , Option "H" ["include-in-header"]
220 (ReqArg
221 (\arg opt -> do
222 text <- readFile arg
223 return opt { optIncludeInHeader = text,
224 optStandalone = True })
225 "FILENAME")
226 "File to include at end of header (implies -s)"
228 , Option "B" ["include-before-body"]
229 (ReqArg
230 (\arg opt -> do
231 text <- readFile arg
232 return opt { optIncludeBeforeBody = text })
233 "FILENAME")
234 "File to include before document body"
236 , Option "A" ["include-after-body"]
237 (ReqArg
238 (\arg opt -> do
239 text <- readFile arg
240 return opt { optIncludeAfterBody = text })
241 "FILENAME")
242 "File to include after document body"
244 , Option "" ["custom-header"]
245 (ReqArg
246 (\arg opt -> do
247 text <- readFile arg
248 return opt { optCustomHeader = text,
249 optStandalone = True })
250 "FILENAME")
251 "File to use for custom header (implies -s)"
253 , Option "T" ["title-prefix"]
254 (ReqArg
255 (\arg opt -> return opt { optTitlePrefix = arg,
256 optStandalone = True })
257 "STRING")
258 "String to prefix to HTML window title"
260 , Option "D" ["print-default-header"]
261 (ReqArg
262 (\arg opt -> do
263 let header = case (lookup arg writers) of
264 Just (writer, head) -> head
265 Nothing -> error ("Unknown reader: " ++ arg)
266 hPutStrLn stdout header
267 exitWith ExitSuccess)
268 "FORMAT")
269 "Print default header for FORMAT"
270 ]
271main = do
273 args <- getArgs
274 let (actions, sources, errors) = getOpt RequireOrder options args
276 -- thread option data structure through all supplied option actions
277 opts <- foldl (>>=) (return startOpt) actions
279 let Opt { optPreserveTabs = preserveTabs
280 , optTabStop = tabStop
281 , optStandalone = standalone
282 , optReader = reader
283 , optWriter = writer
284 , optParseRaw = parseRaw
285 , optCSS = css
286 , optIncludeInHeader = includeHeader
287 , optIncludeBeforeBody = includeBefore
288 , optIncludeAfterBody = includeAfter
289 , optCustomHeader = customHeader
290 , optDefaultHeader = defaultHeader
291 , optTitlePrefix = titlePrefix
292 , optNumberSections = numberSections
293 , optIncremental = incremental
294 , optSmart = smart
295 , optASCIIMathML = asciiMathML
296 } = opts
298 let writingS5 = (defaultHeader == defaultS5Header)
299 let tabFilter = if preserveTabs then id else (tabsToSpaces tabStop)
300 let addBlank str = str ++ "\n\n"
301 let removeCRs str = filter (/= '\r') str -- remove DOS-style line endings
302 let filter = tabFilter . addBlank . removeCRs
303 let startParserState = defaultParserState { stateParseRaw = parseRaw,
304 stateTabStop = tabStop,
305 stateStandalone = standalone }
306 let csslink = if (css == "")
307 then ""
308 else "<link rel=\"stylesheet\" href=\"" ++ css ++
309 "\" type=\"text/css\" media=\"all\" />\n"
310 let asciiMathMLLink = if asciiMathML then asciiMathMLScript else ""
311 let header = (if (customHeader == "DEFAULT")
312 then defaultHeader
313 else customHeader) ++
314 csslink ++ asciiMathMLLink ++ includeHeader
315 let writerOptions = WriterOptions { writerStandalone = standalone,
316 writerHeader = header,
317 writerTitlePrefix = titlePrefix,
318 writerSmart = smart,
319 writerTabStop = tabStop,
320 writerS5 = writingS5,
321 writerIncremental = incremental,
322 writerNumberSections = numberSections,
323 writerIncludeBefore = includeBefore,
324 writerIncludeAfter = includeAfter }
326 (readSources sources) >>= (putStr . encodeUTF8 . (writer writerOptions) .
327 (reader startParserState) . filter .
328 decodeUTF8 . (joinWithSep "\n"))
330 where
331 readSources [] = mapM readSource ["-"]
332 readSources sources = mapM readSource sources
333 readSource "-" = getContents
334 readSource source = readFile source