3Copyright (C) 2006-8 John MacFarlane <jgm@berkeley.edu>
5This program is free software; you can redistribute it and/or modify
6it under the terms of the GNU General Public License as published by
7the Free Software Foundation; either version 2 of the License, or
8(at your option) any later version.
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 Copyright : Copyright (C) 2006-8 John MacFarlane
23 License : GNU GPL, version 2 or above
25 Maintainer : John MacFarlane <jgm@berkeley@edu>
27 Portability : portable
29Parses command-line options and calls the appropriate readers and
35import Text.Pandoc.Shared ( joinWithSep, HTMLMathMethod (..) )
36import Text.Pandoc.Highlighting ( languages )
37import System.Environment ( getArgs, getProgName, getEnvironment )
38import System.Exit ( exitWith, ExitCode (..) )
39import System.FilePath ( takeExtension, takeDirectory )
40import System.Console.GetOpt
41import Data.Maybe ( fromMaybe )
42import Data.Char ( toLower )
43import Prelude hiding ( putStrLn, writeFile, readFile, getContents )
44import System.IO ( stdout, stderr )
48import Text.Pandoc.UTF8
52import Text.Pandoc.Biblio
55copyrightMessage :: String
56copyrightMessage = "\nCopyright (C) 2006-7 John MacFarlane\n" ++
57 "Web: http://johnmacfarlane.net/pandoc\n" ++
58 "This is free software; see the source for copying conditions. There is no\n" ++
59 "warranty, not even for merchantability or fitness for a particular purpose."
80 else "\nCompiled with syntax highlighting support for:\n" ++
81 (unlines $ map unwords $ chunk 5 $ map (\s -> s ++ replicate (15 - length s) ' ') languages)
83-- | Splits a list into groups of at most n.
84chunk :: Int -> [a] -> [[a]]
86 let (next, rest) = splitAt size lst
87 in if null rest then [next] else next : chunk size rest
89-- | Association list of formats and readers.
90readers :: [(String, ParserState -> String -> Pandoc)]
91readers = [("native" , readPandoc)
92 ,("markdown" , readMarkdown)
95 ,("latex" , readLaTeX)
98-- | Reader for native Pandoc format.
99readPandoc :: ParserState -> String -> Pandoc
100readPandoc _ input = read input
102-- | Association list of formats and pairs of writers and default headers.
103writers :: [ ( String, ( WriterOptions -> Pandoc -> String, String ) ) ]
104writers = [("native" , (writeDoc, ""))
105 ,("html" , (writeHtmlString, ""))
106 ,("s5" , (writeS5String, defaultS5Header))
107 ,("docbook" , (writeDocbook, defaultDocbookHeader))
108 ,("opendocument" , (writeOpenDocument, defaultOpenDocumentHeader))
109 ,("odt" , (writeOpenDocument, defaultOpenDocumentHeader))
110 ,("latex" , (writeLaTeX, defaultLaTeXHeader))
111 ,("context" , (writeConTeXt, defaultConTeXtHeader))
112 ,("texinfo" , (writeTexinfo, ""))
113 ,("man" , (writeMan, ""))
114 ,("markdown" , (writeMarkdown, ""))
115 ,("rst" , (writeRST, ""))
116 ,("mediawiki" , (writeMediaWiki, ""))
117 ,("rtf" , (writeRTF, defaultRTFHeader))
120isNonTextOutput :: String -> Bool
121isNonTextOutput "odt" = True
122isNonTextOutput _ = False
124-- | Writer for Pandoc native format.
125writeDoc :: WriterOptions -> Pandoc -> String
126writeDoc _ = prettyPandoc
128-- | Data structure for command line options.
130 { optPreserveTabs :: Bool -- ^ Convert tabs to spaces
131 , optTabStop :: Int -- ^ Number of spaces per tab
132 , optStandalone :: Bool -- ^ Include header, footer
133 , optReader :: String -- ^ Reader format
134 , optWriter :: String -- ^ Writer format
135 , optParseRaw :: Bool -- ^ Parse unconvertable HTML and TeX
136 , optCSS :: [String] -- ^ CSS file to link to
137 , optTableOfContents :: Bool -- ^ Include table of contents
138 , optIncludeInHeader :: String -- ^ File to include in header
139 , optIncludeBeforeBody :: String -- ^ File to include at top of body
140 , optIncludeAfterBody :: String -- ^ File to include at end of body
141 , optCustomHeader :: String -- ^ Custom header to use, or "DEFAULT"
142 , optTitlePrefix :: String -- ^ Optional prefix for HTML title
143 , optOutputFile :: String -- ^ Name of output file
144 , optNumberSections :: Bool -- ^ Number sections in LaTeX
145 , optIncremental :: Bool -- ^ Use incremental lists in S5
146 , optSmart :: Bool -- ^ Use smart typography
147 , optHTMLMathMethod :: HTMLMathMethod -- ^ Method to print HTML math
148 , optDumpArgs :: Bool -- ^ Output command-line arguments
149 , optIgnoreArgs :: Bool -- ^ Ignore command-line arguments
150 , optStrict :: Bool -- ^ Use strict markdown syntax
151 , optReferenceLinks :: Bool -- ^ Use reference links in writing markdown, rst
152 , optWrapText :: Bool -- ^ Wrap text
153 , optSanitizeHTML :: Bool -- ^ Sanitize HTML
155 , optModsFile :: String
156 , optCslFile :: String
160-- | Defaults for command-line options.
163 { optPreserveTabs = False
165 , optStandalone = False
166 , optReader = "" -- null for default reader
167 , optWriter = "" -- null for default writer
168 , optParseRaw = False
170 , optTableOfContents = False
171 , optIncludeInHeader = ""
172 , optIncludeBeforeBody = ""
173 , optIncludeAfterBody = ""
174 , optCustomHeader = "DEFAULT"
175 , optTitlePrefix = ""
176 , optOutputFile = "-" -- "-" means stdout
177 , optNumberSections = False
178 , optIncremental = False
180 , optHTMLMathMethod = PlainMath
181 , optDumpArgs = False
182 , optIgnoreArgs = False
184 , optReferenceLinks = False
186 , optSanitizeHTML = False
193-- | A list of functions, each transforming the options data structure
194-- in response to a command-line option.
195options :: [OptDescr (Opt -> IO Opt)]
197 [ Option "fr" ["from","read"]
199 (\arg opt -> return opt { optReader = map toLower arg })
201 "" -- ("(" ++ (joinWithSep ", " $ map fst readers) ++ ")")
203 , Option "tw" ["to","write"]
205 (\arg opt -> return opt { optWriter = map toLower arg })
207 "" -- ("(" ++ (joinWithSep ", " $ map fst writers) ++ ")")
209 , Option "s" ["standalone"]
211 (\opt -> return opt { optStandalone = True }))
212 "" -- "Include needed header and footer on output"
214 , Option "o" ["output"]
216 (\arg opt -> return opt { optOutputFile = arg })
218 "" -- "Name of output file"
220 , Option "p" ["preserve-tabs"]
222 (\opt -> return opt { optPreserveTabs = True }))
223 "" -- "Preserve tabs instead of converting to spaces"
225 , Option "" ["tab-stop"]
227 (\arg opt -> return opt { optTabStop = (read arg) } )
229 "" -- "Tab stop (default 4)"
231 , Option "" ["strict"]
233 (\opt -> return opt { optStrict = True } ))
234 "" -- "Disable markdown syntax extensions"
236 , Option "" ["reference-links"]
238 (\opt -> return opt { optReferenceLinks = True } ))
239 "" -- "Use reference links in parsing HTML"
241 , Option "R" ["parse-raw"]
243 (\opt -> return opt { optParseRaw = True }))
244 "" -- "Parse untranslatable HTML codes and LaTeX environments as raw"
246 , Option "S" ["smart"]
248 (\opt -> return opt { optSmart = True }))
249 "" -- "Use smart quotes, dashes, and ellipses"
251 , Option "m" ["asciimathml"]
253 (\arg opt -> return opt { optHTMLMathMethod =
256 "" -- "Use ASCIIMathML script in html output"
258 , Option "" ["mimetex"]
260 (\arg opt -> return opt { optHTMLMathMethod = MimeTeX
261 (fromMaybe "/cgi-bin/mimetex.cgi" arg)})
263 "" -- "Use mimetex for HTML math"
265 , Option "" ["gladtex"]
267 (\opt -> return opt { optHTMLMathMethod = GladTeX }))
268 "" -- "Use gladtex for HTML math"
270 , Option "i" ["incremental"]
272 (\opt -> return opt { optIncremental = True }))
273 "" -- "Make list items display incrementally in S5"
275 , Option "N" ["number-sections"]
277 (\opt -> return opt { optNumberSections = True }))
278 "" -- "Number sections in LaTeX"
280 , Option "" ["no-wrap"]
282 (\opt -> return opt { optWrapText = False }))
283 "" -- "Do not wrap text in output"
285 , Option "" ["sanitize-html"]
287 (\opt -> return opt { optSanitizeHTML = True }))
288 "" -- "Sanitize HTML"
290 , Option "" ["toc", "table-of-contents"]
292 (\opt -> return opt { optTableOfContents = True }))
293 "" -- "Include table of contents"
299 return opt { optCSS = old ++ [arg],
300 optStandalone = True })
302 "" -- "Link to CSS style sheet"
304 , Option "H" ["include-in-header"]
307 let old = optIncludeInHeader opt
309 return opt { optIncludeInHeader = old ++ text,
310 optStandalone = True })
312 "" -- "File to include at end of header (implies -s)"
314 , Option "B" ["include-before-body"]
317 let old = optIncludeBeforeBody opt
319 return opt { optIncludeBeforeBody = old ++ text })
321 "" -- "File to include before document body"
323 , Option "A" ["include-after-body"]
326 let old = optIncludeAfterBody opt
328 return opt { optIncludeAfterBody = old ++ text })
330 "" -- "File to include after document body"
332 , Option "C" ["custom-header"]
336 return opt { optCustomHeader = text,
337 optStandalone = True })
339 "" -- "File to use for custom header (implies -s)"
341 , Option "T" ["title-prefix"]
343 (\arg opt -> return opt { optTitlePrefix = arg,
344 optStandalone = True })
346 "" -- "String to prefix to HTML window title"
348 , Option "D" ["print-default-header"]
351 let header = case (lookup arg writers) of
353 Nothing -> error ("Unknown reader: " ++ arg)
354 hPutStr stdout header
355 exitWith ExitSuccess)
357 "" -- "Print default header for FORMAT"
361 (\arg opt -> return opt { optModsFile = arg} )
366 (\arg opt -> return opt { optCslFile = arg} )
370 , Option "" ["dump-args"]
372 (\opt -> return opt { optDumpArgs = True }))
373 "" -- "Print output filename and arguments to stdout."
375 , Option "" ["ignore-args"]
377 (\opt -> return opt { optIgnoreArgs = True }))
378 "" -- "Ignore command-line arguments."
380 , Option "v" ["version"]
384 hPutStrLn stderr (prg ++ " " ++ pandocVersion ++ compileInfo ++
386 exitWith $ ExitFailure 4))
387 "" -- "Print version"
389 , Option "h" ["help"]
393 hPutStr stderr (usageMessage prg options)
394 exitWith $ ExitFailure 2))
398-- Returns usage message
399usageMessage :: String -> [OptDescr (Opt -> IO Opt)] -> String
400usageMessage programName opts = usageInfo
401 (programName ++ " [OPTIONS] [FILES]" ++ "\nInput formats: " ++
402 (joinWithSep ", " $ map fst readers) ++ "\nOutput formats: " ++
403 (joinWithSep ", " $ map fst writers) ++ "\nOptions:")
406-- Determine default reader based on source file extensions
407defaultReaderName :: [FilePath] -> String
408defaultReaderName [] = "markdown"
409defaultReaderName (x:xs) =
410 case takeExtension (map toLower x) of
418 ".native" -> "native"
419 _ -> defaultReaderName xs
421-- Determine default writer based on output file extension
422defaultWriterName :: FilePath -> String
423defaultWriterName "-" = "html" -- no output file
425 case takeExtension (map toLower x) of
426 "" -> "markdown" -- empty extension
430 ".context" -> "context"
435 ".native" -> "native"
437 ".text" -> "markdown"
439 ".markdown" -> "markdown"
441 ".texinfo" -> "texinfo"
444 ['.',y] | y `elem` ['1'..'9'] -> "man"
452 let compatMode = (prg == "hsmarkdown")
454 let (actions, args, errors) = if compatMode
455 then ([], rawArgs, [])
456 else getOpt Permute options rawArgs
458 if (not (null errors))
461 mapM (\e -> hPutStrLn stderr e) errors
462 hPutStr stderr (usageMessage name options)
463 exitWith $ ExitFailure 2
467 let defaultOpts' = if compatMode
468 then defaultOpts { optReader = "markdown"
473 -- thread option data structure through all supplied option actions
474 opts <- foldl (>>=) (return defaultOpts') actions
476 let Opt { optPreserveTabs = preserveTabs
477 , optTabStop = tabStop
478 , optStandalone = standalone
479 , optReader = readerName
480 , optWriter = writerName
481 , optParseRaw = parseRaw
483 , optTableOfContents = toc
484 , optIncludeInHeader = includeHeader
485 , optIncludeBeforeBody = includeBefore
486 , optIncludeAfterBody = includeAfter
487 , optCustomHeader = customHeader
488 , optTitlePrefix = titlePrefix
489 , optOutputFile = outputFile
490 , optNumberSections = numberSections
491 , optIncremental = incremental
493 , optHTMLMathMethod = mathMethod
494 , optDumpArgs = dumpArgs
495 , optIgnoreArgs = ignoreArgs
497 , optReferenceLinks = referenceLinks
499 , optSanitizeHTML = sanitize
501 , optModsFile = modsFile
502 , optCslFile = cslFile
508 hPutStrLn stdout outputFile
509 mapM (\arg -> hPutStrLn stdout arg) args
510 exitWith $ ExitSuccess
513 let sources = if ignoreArgs then [] else args
515 -- assign reader and writer based on options and filenames
516 let readerName' = if null readerName
517 then defaultReaderName sources
520 let writerName' = if null writerName
521 then defaultWriterName outputFile
524 reader <- case (lookup readerName' readers) of
526 Nothing -> error ("Unknown reader: " ++ readerName')
528 (writer, defaultHeader) <- case (lookup writerName' writers) of
529 Just (w,h) -> return (w, h)
530 Nothing -> error ("Unknown writer: " ++ writerName')
532 environment <- getEnvironment
533 let columns = case lookup "COLUMNS" environment of
534 Just cols -> read cols
535 Nothing -> stateColumns defaultParserState
537 let tabFilter _ [] = ""
538 tabFilter _ ('\n':xs) = '\n':(tabFilter tabStop xs)
539 -- remove DOS line endings
540 tabFilter _ ('\r':'\n':xs) = '\n':(tabFilter tabStop xs)
541 tabFilter _ ('\r':xs) = '\n':(tabFilter tabStop xs)
542 tabFilter spsToNextStop ('\t':xs) =
544 then '\t':(tabFilter tabStop xs)
545 else replicate spsToNextStop ' ' ++ tabFilter tabStop xs
547 x:(tabFilter tabStop xs)
548 tabFilter spsToNextStop (x:xs) =
549 x:(tabFilter (spsToNextStop - 1) xs)
551 let standalone' = (standalone && not strict) || writerName' == "odt"
554 refs <- if null modsFile then return [] else readModsColletionFile modsFile
557 let startParserState =
558 defaultParserState { stateParseRaw = parseRaw,
559 stateTabStop = tabStop,
560 stateSanitizeHTML = sanitize,
561 stateStandalone = standalone',
563 stateCitations = map citeKey refs,
565 stateSmart = smart || writerName' `elem`
566 ["latex", "context"],
567 stateColumns = columns,
568 stateStrict = strict }
569 let csslink = if null css
572 (\f -> "<link rel=\"stylesheet\" href=\"" ++
573 f ++ "\" type=\"text/css\" media=\"all\" />\n")
575 let header = (if (customHeader == "DEFAULT")
577 else customHeader) ++ csslink ++ includeHeader
578 let writerOptions = WriterOptions { writerStandalone = standalone',
579 writerHeader = header,
580 writerTitlePrefix = titlePrefix,
581 writerTabStop = tabStop,
582 writerTableOfContents = toc &&
585 writerHTMLMathMethod = mathMethod,
586 writerS5 = (writerName' == "s5"),
587 writerIgnoreNotes = False,
588 writerIncremental = incremental,
589 writerNumberSections = numberSections,
590 writerIncludeBefore = includeBefore,
591 writerIncludeAfter = includeAfter,
592 writerStrictMarkdown = strict,
593 writerReferenceLinks = referenceLinks,
594 writerWrapText = wrap }
596 let writeOutput = if writerName' == "odt"
597 then if outputFile == "-"
599 hPutStrLn stderr ("Error: Cannot write " ++ writerName ++
600 " output to stdout.\n" ++
601 "Specify an output file using the -o option.")
602 exitWith $ ExitFailure 5
603 else let sourceDirRelative = if null sources
605 else takeDirectory (head sources)
606 in saveOpenDocumentAsODT outputFile sourceDirRelative
607 else if outputFile == "-"
609 else writeFile outputFile . (++ "\n")
611 fmap (reader startParserState . tabFilter tabStop . joinWithSep "\n")
612 (readSources sources) >>=
614 processBiblio cslFile refs >>=
616 writeOutput . writer writerOptions
619 readSources [] = mapM readSource ["-"]
620 readSources sources = mapM readSource sources
621 readSource "-" = getContents
622 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