{-# LANGUAGE CPP #-} import Text.Highlighting.Illuminate import System.Environment import qualified Text.Html as H import qualified Text.XHtml as X import Control.Monad -- Note: ghc >= 6.12 (base >=4.2) supports unicode through iconv -- So we use System.IO.UTF8 only if we have an earlier version #if MIN_VERSION_base(4,2,0) import Prelude import System.IO (hPutStrLn) #else import Prelude hiding (putStr, putStrLn, readFile, getContents, writeFile) import System.IO.UTF8 #endif import System.IO (stderr) import System.Exit import Data.List (intercalate) import Data.Char (toLower) import System.Console.GetOpt data Flag = Help | Fragment | Version | List | NumberLines | StartNum String | Outfile String | Syntax String | Style String | Format String deriving (Eq, Show) cmdOpts :: [OptDescr Flag] cmdOpts = [ Option ['h'] ["help"] (NoArg Help) "show usage message" , Option ['v'] ["version"] (NoArg Version) "print version" , Option ['f'] ["fragment"] (NoArg Fragment) "fragment, without document header" , Option ['l'] ["list"] (NoArg List) "list available language syntaxes" , Option ['n'] ["number"] (NoArg NumberLines) "number lines" , Option [] ["start"] (ReqArg StartNum "NUMBER") "start numbering at" , Option ['o'] ["outfile"] (ReqArg Outfile "FILE") "name of output file" , Option [] ["syntax"] (ReqArg Syntax "SYNTAX") "specify language syntax to use" , Option ['s'] ["style"] (ReqArg Style "STYLE") "specify highlighting style" , Option ['t'] ["to"] (ReqArg Format "FORMAT") "to format [ansi|latex|html|xhtml|htmlcss|xhtmlcss]" ] getSyntax :: [Flag] -> Maybe String getSyntax [] = Nothing getSyntax (Syntax s : _) = Just s getSyntax (_:xs) = getSyntax xs getStartNum :: [Flag] -> Int getStartNum [] = 1 getStartNum (StartNum n : _) = read n getStartNum (_:xs) = getStartNum xs getOutfile :: [Flag] -> String getOutfile [] = "-" getOutfile (Outfile f : _) = f getOutfile (_:xs) = getOutfile xs getFormat :: [Flag] -> String getFormat [] = "ansi" getFormat (Format f : _) = map toLower f getFormat (_:xs) = getFormat xs getStyle :: [Flag] -> String getStyle [] = "colorful" getStyle (Style s : _) = map toLower s getStyle (_:xs) = getStyle xs toUnixLineEndings :: String -> String toUnixLineEndings = filter (/='\r') main :: IO () main = do (opts, fnames, errs) <- getArgs >>= return . getOpt Permute cmdOpts prg <- getProgName let usageHeader = prg ++ " [options] [files...]" when (not . null $ errs) $ ioError (userError $ concat errs ++ usageInfo usageHeader cmdOpts) let showLexer l = putStrLn $ name l ++ " (" ++ intercalate ", " (aliases l) ++ "): " ++ intercalate ", " (filenames l) when (List `elem` opts) $ mapM_ showLexer lexers >> exitWith ExitSuccess when (Help `elem` opts) $ hPutStrLn stderr (usageInfo usageHeader cmdOpts) >> exitWith (ExitFailure 1) when (Version `elem` opts) $ -- TODO add version using cabal putStrLn (prg ++ " " ++ " -- (c) 2010 John MacFarlane") >> exitWith ExitSuccess s <- case fnames of [] -> getContents (x:_) -> readFile x let lexer = case getSyntax opts of Just x -> lexerByName x Nothing -> case fnames of [] -> Nothing (x:_) -> lexerByFilename x let (output, fname) = case getOutfile opts of "-" -> (putStr, "-") f -> (writeFile f, f) let numberlines = NumberLines `elem` opts let startnum = getStartNum opts let fragment = Fragment `elem` opts style <- case getStyle opts of "colorful" -> return colorful "hscolour" -> return hscolour "monochrome" -> return monochrome x -> hPutStrLn stderr ("Unknown style `" ++ x ++ "'") >> exitWith (ExitFailure 3) let options = defaultOptions { optStyle = style , optNumberLines = numberlines , optStartNumber = startnum , optAnchors = numberlines } let inHtml includes x = if fragment then foldr (H.renderHtml' 0) "" (H.getHtmlElements x) else H.renderHtml $ H.header H.<< includes H.+++ H.body H.<< x let inXHtml includes x = if fragment then X.showHtmlFragment x else X.showHtml $ X.header X.<< includes X.+++ X.body X.<< x let addLaTeXHeadFoot x = if fragment then x else unlines [ "\\documentclass{report}" , "\\usepackage{fancyvrb}" , "\\usepackage[usenames,dvipsnames]{color}" , "\\begin{document}" , x , "\\end{document}" ] let tokens = case tokenize lexer $ toUnixLineEndings s of Right toks -> toks Left err -> error $ show err case getFormat opts of "html" -> output $ inHtml [H.thetitle H.<< fname] $ toHtmlInline options tokens "xhtml" -> output $ inXHtml [X.thetitle X.<< fname] $ toXHtmlInline options tokens "htmlcss" -> output $ inHtml [ H.thetitle H.<< fname , H.style H.! [H.thetype "text/css"] H.<< cssFor options ] $ toHtmlCSS options tokens "xhtmlcss" -> output $ inXHtml [ X.thetitle X.<< fname , X.style X.! [X.thetype "text/css"] X.<< cssFor options ] $ toXHtmlCSS options tokens "latex" -> output $ addLaTeXHeadFoot $ toLaTeX options tokens "ansi" -> output $ toANSI options tokens x -> hPutStrLn stderr ("Unknown format `" ++ x ++ "'") >> exitWith (ExitFailure 5)