1 -- | This program mangles a pseudo-LaTeX document into actual LaTeX. 2 -- There are three key changes to the input: 3 -- 4 -- * \\input{foo} is replaced by the contents of the file foo (after 5 -- it, too, is mangled). Note that this is relative to the 6 -- working directory, *not* relative to the file being parsed. 7 -- 8 -- * Anything between \\begin{code} and \\end{code} is deleted. 9 -- Note that this is quite unlike normal literate documentation 10 -- (for which we use Haddock, not LaTeX). 11 -- 12 -- * Some nonstandard pseudo-LaTeX commands are expanded into actual 13 -- LaTeX text. In particular, \\darcsCommand{foo} is replaced by 14 -- LaTeX markup describing the command @foo@. 15 module Preproc ( preproc_main ) where 16 import System.FilePath ( (</>) ) 17 import System.Environment ( getArgs ) 18 import System.Exit ( exitWith, ExitCode(..) ) 19 import Text.Regex ( matchRegex, mkRegex ) 20 import Darcs.Commands ( DarcsCommand(SuperCommand, 21 command_sub_commands, command_name, 22 command_extra_arg_help, command_basic_options, 23 command_advanced_options, command_help, 24 command_description), 25 extract_commands ) 26 import Darcs.Arguments ( options_latex ) 27 import Darcs.Commands.Help ( command_control_list, environmentHelp ) 28 import English ( andClauses ) 29 import ThisVersion ( darcs_version ) 30 31 the_commands :: [DarcsCommand] 32 the_commands = extract_commands command_control_list 33 34 -- | The entry point for this program. The path to the TeX master 35 -- file is supplied as the first argument. Bootstrapping into 36 -- 'preproc' then happens by passing it a pseudo-document that 37 -- contains a single input (include) line. 38 preproc_main :: [String] -> IO () 39 preproc_main args = do 40 if length args < 1 41 then exitWith $ ExitFailure 1 42 else return () 43 putStrLn "%% This file was automatically generated by preproc." 44 c <- preproc ["\\input{"++head args++"}"] 45 mapM_ putStrLn c 46 47 -- | Depending on whether pdflatex or htlatex is to be used, the LaTeX 48 -- output of this program must vary subtly. This procedure returns 49 -- true iff the command-line arguments contain @--html@. 50 am_html :: IO Bool 51 am_html = do args <- getArgs 52 return $ elem "--html" args 53 54 -- | Given a list of input lines in pseudo-LaTeX, return the same 55 -- document in LaTeX. The pseudo-LaTeX lines are replaced, other 56 -- lines are used unmodified. 57 preproc :: [String] -> IO [String] 58 preproc [] = return [] -- Empty input, empty output. 59 preproc ("\\usepackage{html}":ss) = -- only use html package with latex2html 60 do rest <- preproc ss 61 ah <- am_html 62 if ah then return $ "\\usepackage{html}" : rest 63 else return $ "\\usepackage{hyperref}" : rest 64 preproc ("\\begin{code}":ss) = ignore ss 65 where ignore :: [String] -> IO [String] 66 ignore ("\\end{code}":ss') = preproc ss' 67 ignore (_:ss') = ignore ss' 68 ignore [] = return [] 69 preproc ("\\begin{options}":ss) = 70 do rest <- preproc ss 71 ah <- am_html 72 if ah then return $ "\\begin{rawhtml}" : "<div class=\"cmd-opt-hdr\">" : rest 73 else return $ ("\\begin{Verbatim}[frame=lines,xleftmargin=1cm," ++ 74 "xrightmargin=1cm]") : rest 75 preproc ("\\end{options}":ss) = 76 do rest <- preproc ss 77 ah <- am_html 78 if ah then return $ "</div>" : "\\end{rawhtml}" : rest 79 else return $ "\\end{Verbatim}" : rest 80 preproc ("\\darcsVersion":ss) = do 81 rest <- preproc ss 82 return $ darcs_version:rest 83 preproc (s:ss) = do 84 rest <- preproc ss 85 let rx = mkRegex "^\\\\(input|darcs(Command|Env))\\{(.+)\\}$" 86 case matchRegex rx s of 87 Just ["input", _, path] -> 88 do cs <- readFile $ "src" </> path -- ratify readFile: not part of darcs executable 89 this <- preproc $ lines cs 90 return $ this ++ rest 91 Just ["darcsCommand", _, command] -> 92 return $ commandHelp command : rest 93 Just ["darcsEnv", _, variable] -> 94 return $ envHelp variable : rest 95 -- The base case for the whole preproc function. Nothing to 96 -- mangle, so this is an ordinary line of TeX, and we append it to 97 -- the result unmodified. 98 _ -> return $ s : rest 99 100 commandHelp :: String -> String 101 commandHelp command = section ++ "{darcs " ++ command ++ "}\n" ++ 102 "\\label{" ++ command ++ "}\n" ++ 103 gh ++ get_options command ++ gd 104 where 105 section = if ' ' `elem` command then "\\subsubsection" else "\\subsection" 106 -- | Given a Darcs command name as a string, return that command's (multi-line) help string. 107 gh :: String 108 gh = escape_latex_specials $ command_property command_help the_commands command 109 -- | Given a Darcs command name as a string, return that command's (one-line) description string. 110 gd :: String 111 gd = command_property command_description the_commands command 112 113 get_options :: String -> String 114 get_options comm = get_com_options $ get_c names the_commands 115 where names = words comm 116 117 get_c :: [String] -> [DarcsCommand] -> [DarcsCommand] 118 get_c (name:ns) commands = 119 case ns of 120 [] -> [get name commands] 121 _ -> case get name commands of 122 c@SuperCommand { } -> 123 c:(get_c ns $ extract_commands $ command_sub_commands c) 124 _ -> 125 error $ "Not a supercommand: " ++ name 126 where get n (c:cs) | command_name c == n = c 127 | otherwise = get n cs 128 get n [] = error $ "No such command: "++n 129 get_c [] _ = error "no command specified" 130 131 get_com_options :: [DarcsCommand] -> String 132 get_com_options c = 133 "\\par\\verb!Usage: darcs " ++ cmd ++ " [OPTION]... " ++ 134 args ++ "!\n\n" ++ "Options:\n\n" ++ options_latex opts1 ++ 135 (if null opts2 then "" else "\n\n" ++ "Advanced options:\n\n" ++ options_latex opts2) 136 where cmd = unwords $ map command_name c 137 args = unwords $ command_extra_arg_help $ last c 138 opts1 = command_basic_options $ last c 139 opts2 = command_advanced_options $ last c 140 141 command_property :: (DarcsCommand -> String) -> [DarcsCommand] -> String 142 -> String 143 command_property property commands name = 144 property $ last c 145 where names = words name 146 c = get_c names commands 147 148 149 150 envHelp :: String -> String 151 envHelp var = unlines $ render $ entry environmentHelp 152 where render (ks, ds) = 153 ("\\paragraph{" ++ escape_latex_specials (andClauses ks) ++ "}") : 154 ("\\label{env:" ++ var ++ "}") : 155 map escape_latex_specials ds 156 entry [] = undefined 157 entry (x:xs) | elem var $ fst x = x 158 | otherwise = entry xs 159 160 -- | LaTeX treats a number of characters or sequences specially. 161 -- Therefore when including ordinary help text in a LaTeX document, it 162 -- is necessary to escape these characters in the way LaTeX expects. 163 escape_latex_specials :: String -> String 164 -- Order is important 165 escape_latex_specials = 166 (bs2 . amp . percent . carrot . dollar . underscore . rbrace . lbrace . bs1) 167 where 168 amp = replace "&" "\\&" 169 bs1 = replace "\\" "\001" 170 bs2 = replace "\001" "$\\backslash$" 171 carrot = replace "^" "\\^{}" 172 dollar = replace "$" "\\$" 173 lbrace = replace "{" "\\{" 174 percent = replace "%" "\\%" 175 rbrace = replace "}" "\\}" 176 underscore = replace "_" "\\_" 177 178 replace :: Eq a => [a] -> [a] -> [a] -> [a] 179 replace _ _ [] = [] 180 replace find repl s = 181 if take (length find) s == find 182 then repl ++ (replace find repl (drop (length find) s)) 183 else [head s] ++ replace find repl (tail s)