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
21 Copyright : Copyright (C) 2006-7 John MacFarlane
22 License : GNU GPL, version 2 or above
24 Maintainer : John MacFarlane <jgm@berkeley@edu>
26 Portability : portable
28Parses command-line options and calls the appropriate readers and
33import Text.Pandoc.UTF8
34import Text.Pandoc.Shared ( joinWithSep )
35import Text.Regex ( mkRegex, matchRegex )
36import System.Environment ( getArgs, getProgName, getEnvironment )
37import System.Exit ( exitWith, ExitCode (..) )
38import System.Console.GetOpt
40import Data.Maybe ( fromMaybe )
41import Data.List ( isPrefixOf )
42import Data.Char ( toLower )
43import Control.Monad ( (>>=) )
48copyrightMessage :: String
49copyrightMessage = "\nCopyright (C) 2006-7 John MacFarlane\n\
50 \Web: http://johnmacfarlane.net/pandoc\n\
51 \This is free software; see the source for copying conditions. There is no\n\
52 \warranty, not even for merchantability or fitness for a particular purpose."
54-- | Association list of formats and readers.
55readers :: [(String, ParserState -> String -> Pandoc)]
56readers = [("native" , readPandoc)
57 ,("markdown" , readMarkdown)
60 ,("latex" , readLaTeX)
63-- | Reader for native Pandoc format.
64readPandoc :: ParserState -> String -> Pandoc
65readPandoc state input = read input
67-- | Association list of formats and pairs of writers and default headers.
68writers :: [ ( String, ( WriterOptions -> Pandoc -> String, String ) ) ]
69writers = [("native" , (writeDoc, ""))
70 ,("html" , (writeHtmlString, ""))
71 ,("s5" , (writeS5String, defaultS5Header))
72 ,("docbook" , (writeDocbook, defaultDocbookHeader))
73 ,("latex" , (writeLaTeX, defaultLaTeXHeader))
74 ,("context" , (writeConTeXt, defaultConTeXtHeader))
75 ,("man" , (writeMan, ""))
76 ,("markdown" , (writeMarkdown, ""))
77 ,("rst" , (writeRST, ""))
78 ,("rtf" , (writeRTF, defaultRTFHeader))
81-- | Writer for Pandoc native format.
82writeDoc :: WriterOptions -> Pandoc -> String
83writeDoc options = prettyPandoc
85-- | Data structure for command line options.
87 { optPreserveTabs :: Bool -- ^ Convert tabs to spaces
88 , optTabStop :: Int -- ^ Number of spaces per tab
89 , optStandalone :: Bool -- ^ Include header, footer
90 , optReader :: String -- ^ Reader format
91 , optWriter :: String -- ^ Writer format
92 , optParseRaw :: Bool -- ^ Parse unconvertable HTML and TeX
93 , optCSS :: String -- ^ CSS file to link to
94 , optTableOfContents :: Bool -- ^ Include table of contents
95 , optIncludeInHeader :: String -- ^ File to include in header
96 , optIncludeBeforeBody :: String -- ^ File to include at top of body
97 , optIncludeAfterBody :: String -- ^ File to include at end of body
98 , optCustomHeader :: String -- ^ Custom header to use, or "DEFAULT"
99 , optTitlePrefix :: String -- ^ Optional prefix for HTML title
100 , optOutputFile :: String -- ^ Name of output file
101 , optNumberSections :: Bool -- ^ Number sections in LaTeX
102 , optIncremental :: Bool -- ^ Use incremental lists in S5
103 , optSmart :: Bool -- ^ Use smart typography
104 , optUseASCIIMathML :: Bool -- ^ Use ASCIIMathML
105 , optASCIIMathMLURL :: Maybe String -- ^ URL to ASCIIMathML.js
106 , optDumpArgs :: Bool -- ^ Output command-line arguments
107 , optIgnoreArgs :: Bool -- ^ Ignore command-line arguments
108 , optStrict :: Bool -- ^ Use strict markdown syntax
109 , optReferenceLinks :: Bool -- ^ Use reference links in writing markdown, rst
110 , optWrapText :: Bool -- ^ Wrap text
113-- | Defaults for command-line options.
116 { optPreserveTabs = False
118 , optStandalone = False
119 , optReader = "" -- null for default reader
120 , optWriter = "" -- null for default writer
121 , optParseRaw = False
123 , optTableOfContents = False
124 , optIncludeInHeader = ""
125 , optIncludeBeforeBody = ""
126 , optIncludeAfterBody = ""
127 , optCustomHeader = "DEFAULT"
128 , optTitlePrefix = ""
129 , optOutputFile = "-" -- "-" means stdout
130 , optNumberSections = False
131 , optIncremental = False
133 , optUseASCIIMathML = False
134 , optASCIIMathMLURL = Nothing
135 , optDumpArgs = False
136 , optIgnoreArgs = False
138 , optReferenceLinks = False
142-- | A list of functions, each transforming the options data structure
143-- in response to a command-line option.
144options :: [OptDescr (Opt -> IO Opt)]
146 [ Option "fr" ["from","read"]
148 (\arg opt -> return opt { optReader = map toLower arg })
150 "" -- ("(" ++ (joinWithSep ", " $ map fst readers) ++ ")")
152 , Option "tw" ["to","write"]
154 (\arg opt -> return opt { optWriter = map toLower arg })
156 "" -- ("(" ++ (joinWithSep ", " $ map fst writers) ++ ")")
158 , Option "s" ["standalone"]
160 (\opt -> return opt { optStandalone = True }))
161 "" -- "Include needed header and footer on output"
163 , Option "o" ["output"]
165 (\arg opt -> return opt { optOutputFile = arg })
167 "" -- "Name of output file"
169 , Option "p" ["preserve-tabs"]
171 (\opt -> return opt { optPreserveTabs = True }))
172 "" -- "Preserve tabs instead of converting to spaces"
174 , Option "" ["tab-stop"]
176 (\arg opt -> return opt { optTabStop = (read arg) } )
178 "" -- "Tab stop (default 4)"
180 , Option "" ["strict"]
182 (\opt -> return opt { optStrict = True } ))
183 "" -- "Disable markdown syntax extensions"
185 , Option "" ["reference-links"]
187 (\opt -> return opt { optReferenceLinks = True } ))
188 "" -- "Use reference links in parsing HTML"
190 , Option "R" ["parse-raw"]
192 (\opt -> return opt { optParseRaw = True }))
193 "" -- "Parse untranslatable HTML codes and LaTeX environments as raw"
195 , Option "S" ["smart"]
197 (\opt -> return opt { optSmart = True }))
198 "" -- "Use smart quotes, dashes, and ellipses"
200 , Option "m" ["asciimathml"]
202 (\arg opt -> return opt { optUseASCIIMathML = True,
203 optASCIIMathMLURL = arg,
204 optStandalone = True })
206 "" -- "Use ASCIIMathML script in html output"
208 , Option "i" ["incremental"]
210 (\opt -> return opt { optIncremental = True }))
211 "" -- "Make list items display incrementally in S5"
213 , Option "N" ["number-sections"]
215 (\opt -> return opt { optNumberSections = True }))
216 "" -- "Number sections in LaTeX"
218 , Option "" ["no-wrap"]
220 (\opt -> return opt { optWrapText = False }))
221 "" -- "Do not wrap text in output"
223 , Option "" ["toc", "table-of-contents"]
225 (\opt -> return opt { optTableOfContents = True }))
226 "" -- "Include table of contents"
230 (\arg opt -> return opt { optCSS = arg,
231 optStandalone = True })
233 "" -- "Link to CSS style sheet"
235 , Option "H" ["include-in-header"]
239 return opt { optIncludeInHeader = fromUTF8 text,
240 optStandalone = True })
242 "" -- "File to include at end of header (implies -s)"
244 , Option "B" ["include-before-body"]
248 return opt { optIncludeBeforeBody = fromUTF8 text })
250 "" -- "File to include before document body"
252 , Option "A" ["include-after-body"]
256 return opt { optIncludeAfterBody = fromUTF8 text })
258 "" -- "File to include after document body"
260 , Option "C" ["custom-header"]
264 return opt { optCustomHeader = fromUTF8 text,
265 optStandalone = True })
267 "" -- "File to use for custom header (implies -s)"
269 , Option "T" ["title-prefix"]
271 (\arg opt -> return opt { optTitlePrefix = arg,
272 optStandalone = True })
274 "" -- "String to prefix to HTML window title"
276 , Option "D" ["print-default-header"]
279 let header = case (lookup arg writers) of
280 Just (writer, head) -> head
281 Nothing -> error ("Unknown reader: " ++ arg)
282 hPutStr stdout header
283 exitWith ExitSuccess)
285 "" -- "Print default header for FORMAT"
287 , Option "" ["dump-args"]
289 (\opt -> return opt { optDumpArgs = True }))
290 "" -- "Print output filename and arguments to stdout."
292 , Option "" ["ignore-args"]
294 (\opt -> return opt { optIgnoreArgs = True }))
295 "" -- "Ignore command-line arguments."
297 , Option "v" ["version"]
301 hPutStrLn stderr (prg ++ " " ++ version ++
303 exitWith $ ExitFailure 4))
304 "" -- "Print version"
306 , Option "h" ["help"]
310 hPutStr stderr (usageMessage prg options)
311 exitWith $ ExitFailure 2))
315-- Returns usage message
316usageMessage :: String -> [OptDescr (Opt -> IO Opt)] -> String
317usageMessage programName options = usageInfo
318 (programName ++ " [OPTIONS] [FILES]" ++ "\nInput formats: " ++
319 (joinWithSep ", " $ map fst readers) ++ "\nOutput formats: " ++
320 (joinWithSep ", " $ map fst writers) ++ "\nOptions:")
323-- Determine default reader based on source file extensions
324defaultReaderName :: [String] -> String
325defaultReaderName [] = "markdown"
326defaultReaderName (x:xs) =
327 let x' = map toLower x in
328 case (matchRegex (mkRegex ".*\\.(.*)") x') of
329 Nothing -> defaultReaderName xs -- no extension
330 Just ["xhtml"] -> "html"
331 Just ["html"] -> "html"
332 Just ["htm"] -> "html"
333 Just ["tex"] -> "latex"
334 Just ["latex"] -> "latex"
335 Just ["ltx"] -> "latex"
336 Just ["rst"] -> "rst"
337 Just ["native"] -> "native"
340-- Determine default writer based on output file extension
341defaultWriterName :: String -> String
342defaultWriterName "-" = "html" -- no output file
344 let x' = map toLower x in
345 case (matchRegex (mkRegex ".*\\.(.*)") x') of
346 Nothing -> "markdown" -- no extension
347 Just [""] -> "markdown" -- empty extension
348 Just ["tex"] -> "latex"
349 Just ["latex"] -> "latex"
350 Just ["ltx"] -> "latex"
351 Just ["context"] -> "context"
352 Just ["ctx"] -> "context"
353 Just ["rtf"] -> "rtf"
354 Just ["rst"] -> "rst"
356 Just ["native"] -> "native"
357 Just ["txt"] -> "markdown"
358 Just ["text"] -> "markdown"
359 Just ["md"] -> "markdown"
360 Just ["markdown"] -> "markdown"
361 Just ["db"] -> "docbook"
362 Just ["xml"] -> "docbook"
363 Just ["sgml"] -> "docbook"
364 Just [[x]] | x `elem` ['1'..'9'] -> "man"
371 let compatMode = (prg == "hsmarkdown")
373 let (actions, args, errors) = if compatMode
374 then ([], rawArgs, [])
375 else getOpt Permute options rawArgs
377 if (not (null errors))
380 mapM (\e -> hPutStrLn stderr e) errors
381 hPutStr stderr (usageMessage name options)
382 exitWith $ ExitFailure 2
386 let defaultOpts' = if compatMode
387 then defaultOpts { optReader = "markdown"
392 -- thread option data structure through all supplied option actions
393 opts <- foldl (>>=) (return defaultOpts') actions
395 let Opt { optPreserveTabs = preserveTabs
396 , optTabStop = tabStop
397 , optStandalone = standalone
398 , optReader = readerName
399 , optWriter = writerName
400 , optParseRaw = parseRaw
402 , optTableOfContents = toc
403 , optIncludeInHeader = includeHeader
404 , optIncludeBeforeBody = includeBefore
405 , optIncludeAfterBody = includeAfter
406 , optCustomHeader = customHeader
407 , optTitlePrefix = titlePrefix
408 , optOutputFile = outputFile
409 , optNumberSections = numberSections
410 , optIncremental = incremental
412 , optUseASCIIMathML = useAsciiMathML
413 , optASCIIMathMLURL = asciiMathMLURL
414 , optDumpArgs = dumpArgs
415 , optIgnoreArgs = ignoreArgs
417 , optReferenceLinks = referenceLinks
423 hPutStrLn stdout outputFile
424 mapM (\arg -> hPutStrLn stdout arg) args
425 exitWith $ ExitSuccess
428 let sources = if ignoreArgs then [] else args
430 -- assign reader and writer based on options and filenames
431 let readerName' = if null readerName
432 then defaultReaderName sources
435 let writerName' = if null writerName
436 then defaultWriterName outputFile
439 reader <- case (lookup readerName' readers) of
441 Nothing -> error ("Unknown reader: " ++ readerName')
443 (writer, defaultHeader) <- case (lookup writerName' writers) of
444 Just (w,h) -> return (w, h)
445 Nothing -> error ("Unknown writer: " ++ writerName')
447 output <- if (outputFile == "-")
449 else openFile outputFile WriteMode
451 environment <- getEnvironment
452 let columns = case lookup "COLUMNS" environment of
453 Just cols -> read cols
454 Nothing -> stateColumns defaultParserState
456 let tabFilter _ [] = ""
457 tabFilter _ ('\n':xs) = '\n':(tabFilter tabStop xs)
458 -- remove DOS line endings
459 tabFilter _ ('\r':'\n':xs) = '\n':(tabFilter tabStop xs)
460 tabFilter _ ('\r':xs) = '\n':(tabFilter tabStop xs)
461 tabFilter spsToNextStop ('\t':xs) =
463 then '\t':(tabFilter tabStop xs)
464 else replicate spsToNextStop ' ' ++ tabFilter tabStop xs
466 x:(tabFilter tabStop xs)
467 tabFilter spsToNextStop (x:xs) =
468 x:(tabFilter (spsToNextStop - 1) xs)
470 let startParserState =
471 defaultParserState { stateParseRaw = parseRaw,
472 stateTabStop = tabStop,
473 stateStandalone = standalone && (not strict),
474 stateSmart = smart || writerName' `elem`
475 ["latex", "context"],
476 stateColumns = columns,
477 stateStrict = strict }
478 let csslink = if (css == "")
480 else "<link rel=\"stylesheet\" href=\"" ++ css ++
481 "\" type=\"text/css\" media=\"all\" />\n"
482 let header = (if (customHeader == "DEFAULT")
484 else customHeader) ++ csslink ++ includeHeader
485 let writerOptions = WriterOptions { writerStandalone = standalone &&
487 writerHeader = header,
488 writerTitlePrefix = titlePrefix,
489 writerTabStop = tabStop,
490 writerTableOfContents = toc &&
493 writerUseASCIIMathML = useAsciiMathML,
494 writerASCIIMathMLURL = asciiMathMLURL,
495 writerS5 = (writerName=="s5"),
496 writerIgnoreNotes = False,
497 writerIncremental = incremental,
498 writerNumberSections = numberSections,
499 writerIncludeBefore = includeBefore,
500 writerIncludeAfter = includeAfter,
501 writerStrictMarkdown = strict,
502 writerReferenceLinks = referenceLinks,
503 writerWrapText = wrap }
505 (readSources sources) >>= (hPutStrLn output . toUTF8 .
506 (writer writerOptions) .
507 (reader startParserState) . tabFilter tabStop .
508 fromUTF8 . (joinWithSep "\n")) >>
512 readSources [] = mapM readSource ["-"]
513 readSources sources = mapM readSource sources
514 readSource "-" = getContents
515 readSource source = readFile source
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list