@@ -45,7 +45,7 @@ import Text.Pandoc.Writers.DefaultHeaders ( defaultHtmlHeader,
4545 defaultRTFHeader, defaultS5Header, defaultLaTeXHeader )
4646import Text.Pandoc.Definition
4747import Text.Pandoc.Shared
48−import Text.Regex ( mkRegex, splitRegex )
48+import Text.Regex ( mkRegex, matchRegex )
4949import System ( exitWith, getArgs, getProgName )
5050import System.Exit
5151import System.Console.GetOpt
@@ -94,8 +94,8 @@ data Opt = Opt
9494 { optPreserveTabs :: Bool -- ^ If @False@, convert tabs to spaces
9595 , optTabStop :: Int -- ^ Number of spaces per tab
9696 , optStandalone :: Bool -- ^ If @True@, include header, footer
97− , optReader :: ParserState -> String -> Pandoc -- ^ Read format
98− , optWriter :: WriterOptions -> Pandoc -> String -- ^ Write fmt
97+ , optReader :: String -- ^ Reader format
98+ , optWriter :: String -- ^ Writer format
9999 , optParseRaw :: Bool -- ^ If @True@, parse unconvertable
100100 -- HTML and TeX
101101 , optCSS :: String -- ^ CSS file to link to
@@ -103,64 +103,55 @@ data Opt = Opt
103103 , optIncludeBeforeBody :: String -- ^ File to include at top of body
104104 , optIncludeAfterBody :: String -- ^ File to include at end of body
105105 , optCustomHeader :: String -- ^ Custom header to use, or "DEFAULT"
106− , optDefaultHeader :: String -- ^ Default header
107106 , optTitlePrefix :: String -- ^ Optional prefix for HTML title
108107 , optOutputFile :: String -- ^ Name of output file
109108 , optNumberSections :: Bool -- ^ If @True@, number sections in LaTeX
110109 , optIncremental :: Bool -- ^ If @True@, incremental lists in S5
111110 , optSmart :: Bool -- ^ If @True@, use smart typography
112111 , optASCIIMathML :: Bool -- ^ If @True@, use ASCIIMathML in HTML
113− , optShowUsage :: Bool -- ^ If @True@, show usage message
114112 , optDebug :: Bool -- ^ If @True@, output debug messages
115113 }
116114
117115-- | Defaults for command-line options.
118−startOpt :: Opt
119−startOpt = Opt
116+defaultOpts :: Opt
117+defaultOpts = Opt
120118 { optPreserveTabs = False
121119 , optTabStop = 4
122120 , optStandalone = False
123− , optReader = readMarkdown
124− , optWriter = writeHtml
121+ , optReader = "" -- null for default reader
122+ , optWriter = "" -- null for default writer
125123 , optParseRaw = False
126124 , optCSS = ""
127125 , optIncludeInHeader = ""
128126 , optIncludeBeforeBody = ""
129127 , optIncludeAfterBody = ""
130128 , optCustomHeader = "DEFAULT"
131− , optDefaultHeader = defaultHtmlHeader
132129 , optTitlePrefix = ""
133130 , optOutputFile = "" -- null for stdout
134131 , optNumberSections = False
135132 , optIncremental = False
136133 , optSmart = False
137134 , optASCIIMathML = False
138− , optShowUsage = False
139135 , optDebug = False
140136 }
141137
142−-- | A list of functions, each transforming the options data structure in response
143−-- to a command-line option.
144−allOptions :: [OptDescr (Opt -> IO Opt)]
145−allOptions =
138+-- | A list of functions, each transforming the options data structure
139+-- in response to a command-line option.
140+options :: [OptDescr (Opt -> IO Opt)]
141+options =
146142 [ Option "fr" ["from","read"]
147143 (ReqArg
148− (\arg opt -> case (lookup (map toLower arg) readers) of
149− Just reader -> return opt { optReader = reader }
150− Nothing -> error ("Unknown reader: " ++ arg) )
144+ (\arg opt -> return opt { optReader = map toLower arg })
151145 "FORMAT")
152− ("Source format (" ++
153− (concatMap (\(name, fn) -> " " ++ name) readers) ++ " )")
146+ ("Input format (" ++ (joinWithSep ", " (map fst readers)) ++
147+ ")")
154148
155149 , Option "tw" ["to","write"]
156150 (ReqArg
157− (\arg opt -> case (lookup (map toLower arg) writers) of
158− Just (writer, defaultHeader) ->
159− return opt { optWriter = writer,
160− optDefaultHeader = defaultHeader }
161− Nothing -> error ("Unknown writer: " ++ arg) )
151+ (\arg opt -> return opt { optWriter = map toLower arg })
162152 "FORMAT")
163− ("Output format (" ++ (concatMap (\(name, fn) -> " " ++ name) writers) ++ " )")
153+ ("Output format (" ++ (joinWithSep ", " (map fst writers)) ++
154+ ")")
164155
165156 , Option "s" ["standalone"]
166157 (NoArg
@@ -169,8 +160,7 @@ allOptions =
169160
170161 , Option "o" ["output"]
171162 (ReqArg
172− (\arg opt -> do
173− return opt { optOutputFile = arg })
163+ (\arg opt -> return opt { optOutputFile = arg })
174164 "FILENAME")
175165 "Name of output file"
176166
@@ -286,57 +276,66 @@ allOptions =
286276
287277 , Option "h" ["help"]
288278 (NoArg
289− (\opt -> return opt { optShowUsage = True }))
279+ (\_ -> do
280+ prg <- getProgName
281+ hPutStr stderr (reformatUsageInfo $
282+ usageInfo (prg ++ " [OPTIONS] [FILES]") options)
283+ exitWith $ ExitFailure 2))
290284 "Show help"
291285 ]
292286
293−-- parse name of calling program and return default reader and writer descriptions
294−parseProgName name =
295− case (splitRegex (mkRegex "2") (map toLower name)) of
296− [from, to] -> (from, to)
297− _ -> ("markdown", "html")
298−
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,
303− optWriter = writer,
304− optDefaultHeader = header}
305− _ -> start
306−
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
312−
313287-- Reformat usage message so it doesn't wrap illegibly
288+reformatUsageInfo :: String -> String
314289reformatUsageInfo = gsub " *--" " --" .
315290 gsub "(-[A-Za-z0-9]) *--" "\\1, --" .
316291 gsub " *([^- ])" "\n\t\\1"
317292
318−main = do
319−
320− name <- getProgName
321− let (from, to) = parseProgName name
293+-- Determine default reader based on source file extensions
294+defaultReaderName :: [String] -> String
295+defaultReaderName [] = "markdown"
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"
309+
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"
323+ Just ["s5"] -> "s5"
324+ Just ["native"] -> "native"
325+ Just ["txt"] -> "markdown"
326+ Just ["text"] -> "markdown"
327+ Just ["md"] -> "markdown"
328+ Just ["markdown"] -> "markdown"
329+ Just _ -> "html"
322330
323− let irrelevantOptions = if not ('2' `elem` name)
324− then ""
325− else "frtwD" ++
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 "")
330−
331− let options = filter (not . inOptList irrelevantOptions) allOptions
332−
333− let defaultOpts = setDefaultOpts from to startOpt
331+main = do
334332
335333 args <- getArgs
336334 let (actions, sources, errors) = getOpt Permute options args
337335
338336 if (not (null errors))
339337 then do
338+ name <- getProgName
340339 mapM (\e -> hPutStrLn stderr e) errors
341340 hPutStrLn stderr (reformatUsageInfo $
342341 usageInfo (name ++ " [OPTIONS] [FILES]") options)
@@ -350,30 +349,39 @@ main = do
350349 let Opt { optPreserveTabs = preserveTabs
351350 , optTabStop = tabStop
352351 , optStandalone = standalone
353− , optReader = reader
354− , optWriter = writer
352+ , optReader = readerName
353+ , optWriter = writerName
355354 , optParseRaw = parseRaw
356355 , optCSS = css
357356 , optIncludeInHeader = includeHeader
358357 , optIncludeBeforeBody = includeBefore
359358 , optIncludeAfterBody = includeAfter
360359 , optCustomHeader = customHeader
361− , optDefaultHeader = defaultHeader
362360 , optTitlePrefix = titlePrefix
363361 , optOutputFile = outputFile
364362 , optNumberSections = numberSections
365363 , optIncremental = incremental
366364 , optSmart = smart
367365 , optASCIIMathML = asciiMathML
368− , optShowUsage = showUsage
369366 , optDebug = debug
370367 } = opts
371368
372− if showUsage
373− then do
374− hPutStr stderr (reformatUsageInfo $ usageInfo (name ++ " [OPTIONS] [FILES]") options)
375− exitWith $ ExitFailure 2
376− else return ()
369+ -- assign reader and writer based on options and filenames
370+ let readerName' = if null readerName
371+ then defaultReaderName sources
372+ else readerName
373+
374+ let writerName' = if null writerName
375+ then defaultWriterName outputFile
376+ else writerName
377+
378+ reader <- case (lookup readerName' readers) of
379+ Just r -> return r
380+ Nothing -> error ("Unknown reader: " ++ readerName')
381+
382+ (writer, defaultHeader) <- case (lookup writerName' writers) of
383+ Just (w,h) -> return (w, h)
384+ Nothing -> error ("Unknown writer: " ++ writerName')
377385
378386 output <- if ((null outputFile) || debug)
379387 then return stdout
@@ -385,7 +393,6 @@ main = do
385393 hPutStr stderr $ concatMap (\s -> "INPUT=" ++ s ++ "\n") sources
386394 else return ()
387395
388− let writingS5 = (defaultHeader == defaultS5Header)
389396 let tabFilter = if preserveTabs then id else (tabsToSpaces tabStop)
390397 let addBlank str = str ++ "\n\n"
391398 let removeCRs str = filter (/= '\r') str -- remove DOS-style line endings
@@ -407,7 +414,7 @@ main = do
407414 writerTitlePrefix = titlePrefix,
408415 writerSmart = smart,
409416 writerTabStop = tabStop,
410− writerS5 = writingS5,
417+ writerS5 = (writerName=="s5"),
411418 writerIncremental = incremental,
412419 writerNumberSections = numberSections,
413420 writerIncludeBefore = includeBefore,