@@ -45,7 +45,7 @@ import Text.Pandoc.Writers.DefaultHeaders ( defaultHtmlHeader,
45 defaultRTFHeader, defaultS5Header, defaultLaTeXHeader )
45 defaultRTFHeader, defaultS5Header, defaultLaTeXHeader )
46import Text.Pandoc.Definition
46import Text.Pandoc.Definition
47import Text.Pandoc.Shared
47import Text.Pandoc.Shared
48−import Text.Regex ( mkRegex, splitRegex )
48+import Text.Regex ( mkRegex, matchRegex )
49import System ( exitWith, getArgs, getProgName )
49import System ( exitWith, getArgs, getProgName )
50import System.Exit
50import System.Exit
51import System.Console.GetOpt
51import System.Console.GetOpt
@@ -94,8 +94,8 @@ data Opt = Opt
94 { optPreserveTabs :: Bool -- ^ If @False@, convert tabs to spaces
94 { optPreserveTabs :: Bool -- ^ If @False@, convert tabs to spaces
95 , optTabStop :: Int -- ^ Number of spaces per tab
95 , optTabStop :: Int -- ^ Number of spaces per tab
96 , optStandalone :: Bool -- ^ If @True@, include header, footer
96 , optStandalone :: Bool -- ^ If @True@, include header, footer
97− , optReader :: ParserState -> String -> Pandoc -- ^ Read format
97+ , optReader :: String -- ^ Reader format
98− , optWriter :: WriterOptions -> Pandoc -> String -- ^ Write fmt
98+ , optWriter :: String -- ^ Writer format
99 , optParseRaw :: Bool -- ^ If @True@, parse unconvertable
99 , optParseRaw :: Bool -- ^ If @True@, parse unconvertable
100 -- HTML and TeX
100 -- HTML and TeX
101 , optCSS :: String -- ^ CSS file to link to
101 , optCSS :: String -- ^ CSS file to link to
@@ -103,64 +103,55 @@ data Opt = Opt
103 , optIncludeBeforeBody :: String -- ^ File to include at top of body
103 , optIncludeBeforeBody :: String -- ^ File to include at top of body
104 , optIncludeAfterBody :: String -- ^ File to include at end of body
104 , optIncludeAfterBody :: String -- ^ File to include at end of body
105 , optCustomHeader :: String -- ^ Custom header to use, or "DEFAULT"
105 , optCustomHeader :: String -- ^ Custom header to use, or "DEFAULT"
106− , optDefaultHeader :: String -- ^ Default header
107 , optTitlePrefix :: String -- ^ Optional prefix for HTML title
106 , optTitlePrefix :: String -- ^ Optional prefix for HTML title
108 , optOutputFile :: String -- ^ Name of output file
107 , optOutputFile :: String -- ^ Name of output file
109 , optNumberSections :: Bool -- ^ If @True@, number sections in LaTeX
108 , optNumberSections :: Bool -- ^ If @True@, number sections in LaTeX
110 , optIncremental :: Bool -- ^ If @True@, incremental lists in S5
109 , optIncremental :: Bool -- ^ If @True@, incremental lists in S5
111 , optSmart :: Bool -- ^ If @True@, use smart typography
110 , optSmart :: Bool -- ^ If @True@, use smart typography
112 , optASCIIMathML :: Bool -- ^ If @True@, use ASCIIMathML in HTML
111 , optASCIIMathML :: Bool -- ^ If @True@, use ASCIIMathML in HTML
113− , optShowUsage :: Bool -- ^ If @True@, show usage message
114 , optDebug :: Bool -- ^ If @True@, output debug messages
112 , optDebug :: Bool -- ^ If @True@, output debug messages
117-- | Defaults for command-line options.
115-- | Defaults for command-line options.
118−startOpt :: Opt
116+defaultOpts :: Opt
119−startOpt = Opt
117+defaultOpts = Opt
120 { optPreserveTabs = False
118 { optPreserveTabs = False
121 , optTabStop = 4
119 , optTabStop = 4
122 , optStandalone = False
120 , optStandalone = False
123− , optReader = readMarkdown
121+ , optReader = "" -- null for default reader
124− , optWriter = writeHtml
122+ , optWriter = "" -- null for default writer
125 , optParseRaw = False
123 , optParseRaw = False
126 , optCSS = ""
124 , optCSS = ""
127 , optIncludeInHeader = ""
125 , optIncludeInHeader = ""
128 , optIncludeBeforeBody = ""
126 , optIncludeBeforeBody = ""
129 , optIncludeAfterBody = ""
127 , optIncludeAfterBody = ""
130 , optCustomHeader = "DEFAULT"
128 , optCustomHeader = "DEFAULT"
131− , optDefaultHeader = defaultHtmlHeader
132 , optTitlePrefix = ""
129 , optTitlePrefix = ""
133 , optOutputFile = "" -- null for stdout
130 , optOutputFile = "" -- null for stdout
134 , optNumberSections = False
131 , optNumberSections = False
135 , optIncremental = False
132 , optIncremental = False
136 , optSmart = False
133 , optSmart = False
137 , optASCIIMathML = False
134 , optASCIIMathML = False
138− , optShowUsage = False
139 , optDebug = False
135 , optDebug = False
142−-- | A list of functions, each transforming the options data structure in response
138+-- | A list of functions, each transforming the options data structure
143−-- to a command-line option.
139+-- in response to a command-line option.
144−allOptions :: [OptDescr (Opt -> IO Opt)]
140+options :: [OptDescr (Opt -> IO Opt)]
145−allOptions =
141+options =
146 [ Option "fr" ["from","read"]
142 [ Option "fr" ["from","read"]
148− (\arg opt -> case (lookup (map toLower arg) readers) of
149− Just reader -> return opt { optReader = reader }
144+ (\arg opt -> return opt { optReader = map toLower arg })
150− Nothing -> error ("Unknown reader: " ++ arg) )
151 "FORMAT")
145 "FORMAT")
152− ("Source format (" ++
146+ ("Input format (" ++ (joinWithSep ", " (map fst readers)) ++
153− (concatMap (\(name, fn) -> " " ++ name) readers) ++ " )")
147+ ")")
155 , Option "tw" ["to","write"]
149 , Option "tw" ["to","write"]
157− (\arg opt -> case (lookup (map toLower arg) writers) of
158− Just (writer, defaultHeader) ->
159− return opt { optWriter = writer,
151+ (\arg opt -> return opt { optWriter = map toLower arg })
160− optDefaultHeader = defaultHeader }
161− Nothing -> error ("Unknown writer: " ++ arg) )
162 "FORMAT")
152 "FORMAT")
163− ("Output format (" ++ (concatMap (\(name, fn) -> " " ++ name) writers) ++ " )")
153+ ("Output format (" ++ (joinWithSep ", " (map fst writers)) ++
165 , Option "s" ["standalone"]
156 , Option "s" ["standalone"]
@@ -169,8 +160,7 @@ allOptions =
170 , Option "o" ["output"]
161 , Option "o" ["output"]
173− return opt { optOutputFile = arg })
163+ (\arg opt -> return opt { optOutputFile = arg })
174 "FILENAME")
164 "FILENAME")
175 "Name of output file"
165 "Name of output file"
@@ -286,57 +276,66 @@ allOptions =
287 , Option "h" ["help"]
277 , Option "h" ["help"]
289− (\opt -> return opt { optShowUsage = True }))
279+ (\_ -> do
281+ hPutStr stderr (reformatUsageInfo $
282+ usageInfo (prg ++ " [OPTIONS] [FILES]") options)
283+ exitWith $ ExitFailure 2))
290 "Show help"
284 "Show help"
293−-- parse name of calling program and return default reader and writer descriptions
295− case (splitRegex (mkRegex "2") (map toLower name)) of
296− [from, to] -> (from, to)
297− _ -> ("markdown", "html")
299−-- set default options based on reader and writer descriptions; start is starting options
300−setDefaultOpts from to start =
301− case ((lookup from readers), (lookup to writers)) of
302− (Just reader, Just (writer, header)) -> start {optReader = reader,
304− optDefaultHeader = header}
307−-- True if single-letter option is in option list
308−inOptList :: [Char] -> OptDescr (Opt -> IO Opt) -> Bool
309−inOptList list desc =
310− let (Option letters _ _ _) = desc in
311− any (\x -> x `elem` list) letters
313-- Reformat usage message so it doesn't wrap illegibly
287-- Reformat usage message so it doesn't wrap illegibly
288+reformatUsageInfo :: String -> String
314reformatUsageInfo = gsub " *--" " --" .
289reformatUsageInfo = gsub " *--" " --" .
315 gsub "(-[A-Za-z0-9]) *--" "\\1, --" .
290 gsub "(-[A-Za-z0-9]) *--" "\\1, --" .
316 gsub " *([^- ])" "\n\t\\1"
291 gsub " *([^- ])" "\n\t\\1"
318−main = do
293+-- Determine default reader based on source file extensions
319−
294+defaultReaderName :: [String] -> String
320− name <- getProgName
295+defaultReaderName [] = "markdown"
321− let (from, to) = parseProgName name
296+defaultReaderName (x:xs) =
297+ let x' = map toLower x in
298+ case (matchRegex (mkRegex ".*\\.(.*)") x') of
299+ Nothing -> defaultReaderName xs -- no extension
300+ Just ["xhtml"] -> "html"
301+ Just ["html"] -> "html"
302+ Just ["htm"] -> "html"
303+ Just ["tex"] -> "latex"
304+ Just ["latex"] -> "latex"
305+ Just ["ltx"] -> "latex"
306+ Just ["rst"] -> "rst"
307+ Just ["native"] -> "native"
308+ Just _ -> "markdown"
310+-- Determine default writer based on output file extension
311+defaultWriterName :: String -> String
312+defaultWriterName "" = "html" -- no output file
313+defaultWriterName x =
314+ let x' = map toLower x in
315+ case (matchRegex (mkRegex ".*\\.(.*)") x') of
316+ Nothing -> "markdown" -- no extension
317+ Just [""] -> "markdown" -- empty extension
318+ Just ["tex"] -> "latex"
319+ Just ["latex"] -> "latex"
320+ Just ["ltx"] -> "latex"
321+ Just ["rtf"] -> "rtf"
322+ Just ["rst"] -> "rst"
324+ Just ["native"] -> "native"
325+ Just ["txt"] -> "markdown"
326+ Just ["text"] -> "markdown"
327+ Just ["md"] -> "markdown"
328+ Just ["markdown"] -> "markdown"
323− let irrelevantOptions = if not ('2' `elem` name)
331+main = do
326− (if (to /= "html" && to /= "s5") then "SmcT" else "") ++
327− (if (to /= "latex") then "N" else "") ++
328− (if (to /= "s5") then "i" else "") ++
329− (if (from /= "html" && from /= "latex") then "R" else "")
331− let options = filter (not . inOptList irrelevantOptions) allOptions
333− let defaultOpts = setDefaultOpts from to startOpt
335 args <- getArgs
333 args <- getArgs
336 let (actions, sources, errors) = getOpt Permute options args
334 let (actions, sources, errors) = getOpt Permute options args
338 if (not (null errors))
336 if (not (null errors))
340 mapM (\e -> hPutStrLn stderr e) errors
339 mapM (\e -> hPutStrLn stderr e) errors
341 hPutStrLn stderr (reformatUsageInfo $
340 hPutStrLn stderr (reformatUsageInfo $
342 usageInfo (name ++ " [OPTIONS] [FILES]") options)
341 usageInfo (name ++ " [OPTIONS] [FILES]") options)
@@ -350,30 +349,39 @@ main = do
350 let Opt { optPreserveTabs = preserveTabs
349 let Opt { optPreserveTabs = preserveTabs
351 , optTabStop = tabStop
350 , optTabStop = tabStop
352 , optStandalone = standalone
351 , optStandalone = standalone
353− , optReader = reader
352+ , optReader = readerName
354− , optWriter = writer
353+ , optWriter = writerName
355 , optParseRaw = parseRaw
354 , optParseRaw = parseRaw
356 , optCSS = css
355 , optCSS = css
357 , optIncludeInHeader = includeHeader
356 , optIncludeInHeader = includeHeader
358 , optIncludeBeforeBody = includeBefore
357 , optIncludeBeforeBody = includeBefore
359 , optIncludeAfterBody = includeAfter
358 , optIncludeAfterBody = includeAfter
360 , optCustomHeader = customHeader
359 , optCustomHeader = customHeader
361− , optDefaultHeader = defaultHeader
362 , optTitlePrefix = titlePrefix
360 , optTitlePrefix = titlePrefix
363 , optOutputFile = outputFile
361 , optOutputFile = outputFile
364 , optNumberSections = numberSections
362 , optNumberSections = numberSections
365 , optIncremental = incremental
363 , optIncremental = incremental
366 , optSmart = smart
364 , optSmart = smart
367 , optASCIIMathML = asciiMathML
365 , optASCIIMathML = asciiMathML
368− , optShowUsage = showUsage
369 , optDebug = debug
366 , optDebug = debug
372− if showUsage
369+ -- assign reader and writer based on options and filenames
373− then do
370+ let readerName' = if null readerName
374− hPutStr stderr (reformatUsageInfo $ usageInfo (name ++ " [OPTIONS] [FILES]") options)
371+ then defaultReaderName sources
375− exitWith $ ExitFailure 2
372+ else readerName
374+ let writerName' = if null writerName
375+ then defaultWriterName outputFile
378+ reader <- case (lookup readerName' readers) of
376− else return ()
379+ Just r -> return r
380+ Nothing -> error ("Unknown reader: " ++ readerName')
382+ (writer, defaultHeader) <- case (lookup writerName' writers) of
383+ Just (w,h) -> return (w, h)
384+ Nothing -> error ("Unknown writer: " ++ writerName')
378 output <- if ((null outputFile) || debug)
386 output <- if ((null outputFile) || debug)
379 then return stdout
387 then return stdout
@@ -385,7 +393,6 @@ main = do
385 hPutStr stderr $ concatMap (\s -> "INPUT=" ++ s ++ "\n") sources
393 hPutStr stderr $ concatMap (\s -> "INPUT=" ++ s ++ "\n") sources
386 else return ()
394 else return ()
388− let writingS5 = (defaultHeader == defaultS5Header)
389 let tabFilter = if preserveTabs then id else (tabsToSpaces tabStop)
396 let tabFilter = if preserveTabs then id else (tabsToSpaces tabStop)
390 let addBlank str = str ++ "\n\n"
397 let addBlank str = str ++ "\n\n"
391 let removeCRs str = filter (/= '\r') str -- remove DOS-style line endings
398 let removeCRs str = filter (/= '\r') str -- remove DOS-style line endings
@@ -407,7 +414,7 @@ main = do
407 writerTitlePrefix = titlePrefix,
414 writerTitlePrefix = titlePrefix,
408 writerSmart = smart,
415 writerSmart = smart,
409 writerTabStop = tabStop,
416 writerTabStop = tabStop,
410− writerS5 = writingS5,
417+ writerS5 = (writerName=="s5"),
411 writerIncremental = incremental,
418 writerIncremental = incremental,
412 writerNumberSections = numberSections,
419 writerNumberSections = numberSections,
413 writerIncludeBefore = includeBefore,
420 writerIncludeBefore = includeBefore,