1 {-# OPTIONS -fffi -cpp #-}
3 ------------------------------------------------------------------------
4 -- $Id: Main.hs,v 1.62 2005/01/05 11:17:46 malcolm Exp $
6 -- Program for converting .hsc files to .hs files, by converting the
7 -- file into a C program which is run to generate the Haskell source.
8 -- Certain items known only to the C compiler can then be used in
9 -- the Haskell module; for example #defined constants, byte offsets
10 -- within structures, etc.
12 -- See the documentation in the Users' Guide for more details.
14 #if __GLASGOW_HASKELL__ >= 504 || __NHC__ >= 114
15 import System.Console.GetOpt
20 import System (getProgName, getArgs, ExitCode(..), exitWith, system)
21 import Directory (removeFile,doesFileExist)
22 import Monad (MonadPlus(..), liftM, liftM2, when)
23 import Char (isAlpha, isAlphaNum, isSpace, isDigit, toUpper, intToDigit, ord)
24 import List (intersperse, isSuffixOf)
25 import IO (hPutStr, hPutStrLn, stderr)
27 #if defined(mingw32_HOST_OS)
29 #if __GLASGOW_HASKELL__ >= 504 || __NHC__ >= 114
30 import Foreign.C.String
37 #ifdef __GLASGOW_HASKELL__
38 import Compat.RawSystem ( rawSystem )
40 rawSystem prog args = system (prog++unwords args)
44 version = "hsc2hs version 0.66\n"
56 | Define String (Maybe String)
60 template_flag :: Flag -> Bool
61 template_flag (Template _) = True
62 template_flag _ = False
64 include :: String -> Flag
65 include s@('\"':_) = Include s
66 include s@('<' :_) = Include s
67 include s = Include ("\""++s++"\"")
69 define :: String -> Flag
70 define s = case break (== '=') s of
71 (name, []) -> Define name Nothing
72 (name, _:value) -> Define name (Just value)
74 options :: [OptDescr Flag]
76 Option ['o'] ["output"] (ReqArg Output "FILE")
77 "name of main output file",
78 Option ['t'] ["template"] (ReqArg Template "FILE")
80 Option ['c'] ["cc"] (ReqArg Compiler "PROG")
82 Option ['l'] ["ld"] (ReqArg Linker "PROG")
84 Option ['C'] ["cflag"] (ReqArg CompFlag "FLAG")
85 "flag to pass to the C compiler",
86 Option ['I'] [] (ReqArg (CompFlag . ("-I"++)) "DIR")
87 "passed to the C compiler",
88 Option ['L'] ["lflag"] (ReqArg LinkFlag "FLAG")
89 "flag to pass to the linker",
90 Option ['i'] ["include"] (ReqArg include "FILE")
91 "as if placed in the source",
92 Option ['D'] ["define"] (ReqArg define "NAME[=VALUE]")
93 "as if placed in the source",
94 Option [] ["no-compile"] (NoArg NoCompile)
95 "stop after writing *_hsc_make.c",
96 Option ['v'] ["verbose"] (NoArg Verbose)
97 "dump commands to stderr",
98 Option ['?'] ["help"] (NoArg Help)
99 "display this help and exit",
100 Option ['V'] ["version"] (NoArg Version)
101 "output version information and exit" ]
106 prog <- getProgramName
107 let header = "Usage: "++prog++" [OPTIONS] INPUT.hsc [...]\n"
109 let (flags, files, errs) = getOpt Permute options args
111 -- If there is no Template flag explicitly specified, try
112 -- to find one by looking near the executable. This only
113 -- works on Win32 (getExecDir). On Unix, there's a wrapper
114 -- script which specifies an explicit template flag.
115 flags_w_tpl <- if any template_flag flags then
118 do mb_path <- getExecDir "/bin/hsc2hs.exe"
123 let templ = path ++ "/template-hsc.h"
124 flg <- doesFileExist templ
126 then return ((Template templ):)
128 return (add_opt flags)
129 case (files, errs) of
131 | any isHelp flags_w_tpl -> bye (usageInfo header options)
132 | any isVersion flags_w_tpl -> bye version
134 isHelp Help = True; isHelp _ = False
135 isVersion Version = True; isVersion _ = False
136 ((_:_), []) -> mapM_ (processFile flags_w_tpl) files
137 (_, _ ) -> die (concat errs ++ usageInfo header options)
139 getProgramName :: IO String
140 getProgramName = liftM (`withoutSuffix` "-bin") getProgName
141 where str `withoutSuffix` suff
142 | suff `isSuffixOf` str = take (length str - length suff) str
145 bye :: String -> IO a
146 bye s = putStr s >> exitWith ExitSuccess
148 die :: String -> IO a
149 die s = hPutStr stderr s >> exitWith (ExitFailure 1)
151 processFile :: [Flag] -> String -> IO ()
152 processFile flags name
153 = do let file_name = dosifyPath name
154 s <- readFile file_name
156 Parser p -> case p (SourcePos file_name 1) s of
157 Success _ _ _ toks -> output flags file_name toks
158 Failure (SourcePos name' line) msg ->
159 die (name'++":"++show line++": "++msg++"\n")
161 ------------------------------------------------------------------------
162 -- A deterministic parser which remembers the text which has been parsed.
164 newtype Parser a = Parser (SourcePos -> String -> ParseResult a)
166 data ParseResult a = Success !SourcePos String String a
167 | Failure !SourcePos String
169 data SourcePos = SourcePos String !Int
171 updatePos :: SourcePos -> Char -> SourcePos
172 updatePos pos@(SourcePos name line) ch = case ch of
173 '\n' -> SourcePos name (line + 1)
176 instance Monad Parser where
177 return a = Parser $ \pos s -> Success pos [] s a
179 Parser $ \pos s -> case m pos s of
180 Success pos' out1 s' a -> case k a of
181 Parser k' -> case k' pos' s' of
182 Success pos'' out2 imp'' b ->
183 Success pos'' (out1++out2) imp'' b
184 Failure pos'' msg -> Failure pos'' msg
185 Failure pos' msg -> Failure pos' msg
186 fail msg = Parser $ \pos _ -> Failure pos msg
188 instance MonadPlus Parser where
190 Parser m `mplus` Parser n =
191 Parser $ \pos s -> case m pos s of
192 success@(Success _ _ _ _) -> success
193 Failure _ _ -> n pos s
195 getPos :: Parser SourcePos
196 getPos = Parser $ \pos s -> Success pos [] s pos
198 setPos :: SourcePos -> Parser ()
199 setPos pos = Parser $ \_ s -> Success pos [] s ()
201 message :: Parser a -> String -> Parser a
202 Parser m `message` msg =
203 Parser $ \pos s -> case m pos s of
204 success@(Success _ _ _ _) -> success
205 Failure pos' _ -> Failure pos' msg
207 catchOutput_ :: Parser a -> Parser String
208 catchOutput_ (Parser m) =
209 Parser $ \pos s -> case m pos s of
210 Success pos' out s' _ -> Success pos' [] s' out
211 Failure pos' msg -> Failure pos' msg
213 fakeOutput :: Parser a -> String -> Parser a
214 Parser m `fakeOutput` out =
215 Parser $ \pos s -> case m pos s of
216 Success pos' _ s' a -> Success pos' out s' a
217 Failure pos' msg -> Failure pos' msg
219 lookAhead :: Parser String
220 lookAhead = Parser $ \pos s -> Success pos [] s s
222 satisfy :: (Char -> Bool) -> Parser Char
224 Parser $ \pos s -> case s of
225 c:cs | p c -> Success (updatePos pos c) [c] cs c
226 _ -> Failure pos "Bad character"
228 char_ :: Char -> Parser ()
230 satisfy (== c) `message` (show c++" expected")
233 anyChar_ :: Parser ()
235 satisfy (const True) `message` "Unexpected end of file"
238 any2Chars_ :: Parser ()
239 any2Chars_ = anyChar_ >> anyChar_
241 many :: Parser a -> Parser [a]
242 many p = many1 p `mplus` return []
244 many1 :: Parser a -> Parser [a]
245 many1 p = liftM2 (:) p (many p)
247 many_ :: Parser a -> Parser ()
248 many_ p = many1_ p `mplus` return ()
250 many1_ :: Parser a -> Parser ()
251 many1_ p = p >> many_ p
253 manySatisfy, manySatisfy1 :: (Char -> Bool) -> Parser String
254 manySatisfy = many . satisfy
255 manySatisfy1 = many1 . satisfy
257 manySatisfy_, manySatisfy1_ :: (Char -> Bool) -> Parser ()
258 manySatisfy_ = many_ . satisfy
259 manySatisfy1_ = many1_ . satisfy
261 ------------------------------------------------------------------------
262 -- Parser of hsc syntax.
265 = Text SourcePos String
266 | Special SourcePos String String
268 parser :: Parser [Token]
271 t <- catchOutput_ text
275 _:_ -> liftM2 (:) (special `fakeOutput` []) parser
276 return (if null t then rest else Text pos t : rest)
283 c:_ | isAlpha c || c == '_' -> do
285 manySatisfy_ (\c' -> isAlphaNum c' || c' == '_' || c' == '\'')
287 c:_ | isHsSymbol c -> do
288 symb <- catchOutput_ (manySatisfy_ isHsSymbol)
291 '-':'-':symb' | all (== '-') symb' -> do
292 return () `fakeOutput` symb
293 manySatisfy_ (/= '\n')
296 return () `fakeOutput` unescapeHashes symb
298 '\"':_ -> do anyChar_; hsString '\"'; text
299 '\'':_ -> do anyChar_; hsString '\''; text
300 '{':'-':_ -> do any2Chars_; linePragma `mplus` hsComment; text
301 _:_ -> do anyChar_; text
303 hsString :: Char -> Parser ()
308 c:_ | c == quote -> anyChar_
313 char_ '\\' `mplus` return ()
315 | otherwise -> do any2Chars_; hsString quote
316 _:_ -> do anyChar_; hsString quote
318 hsComment :: Parser ()
323 '-':'}':_ -> any2Chars_
324 '{':'-':_ -> do any2Chars_; hsComment; hsComment
325 _:_ -> do anyChar_; hsComment
327 linePragma :: Parser ()
331 satisfy (\c -> c == 'L' || c == 'l')
332 satisfy (\c -> c == 'I' || c == 'i')
333 satisfy (\c -> c == 'N' || c == 'n')
334 satisfy (\c -> c == 'E' || c == 'e')
335 manySatisfy1_ isSpace
336 line <- liftM read $ manySatisfy1 isDigit
337 manySatisfy1_ isSpace
339 name <- manySatisfy (/= '\"')
345 setPos (SourcePos name (line - 1))
347 isHsSymbol :: Char -> Bool
348 isHsSymbol '!' = True; isHsSymbol '#' = True; isHsSymbol '$' = True
349 isHsSymbol '%' = True; isHsSymbol '&' = True; isHsSymbol '*' = True
350 isHsSymbol '+' = True; isHsSymbol '.' = True; isHsSymbol '/' = True
351 isHsSymbol '<' = True; isHsSymbol '=' = True; isHsSymbol '>' = True
352 isHsSymbol '?' = True; isHsSymbol '@' = True; isHsSymbol '\\' = True
353 isHsSymbol '^' = True; isHsSymbol '|' = True; isHsSymbol '-' = True
354 isHsSymbol '~' = True
357 unescapeHashes :: String -> String
358 unescapeHashes [] = []
359 unescapeHashes ('#':'#':s) = '#' : unescapeHashes s
360 unescapeHashes (c:s) = c : unescapeHashes s
362 lookAheadC :: Parser String
363 lookAheadC = liftM joinLines lookAhead
366 joinLines ('\\':'\n':s) = joinLines s
367 joinLines (c:s) = c : joinLines s
369 satisfyC :: (Char -> Bool) -> Parser Char
373 '\\':'\n':_ -> do any2Chars_ `fakeOutput` []; satisfyC p
376 charC_ :: Char -> Parser ()
378 satisfyC (== c) `message` (show c++" expected")
381 anyCharC_ :: Parser ()
383 satisfyC (const True) `message` "Unexpected end of file"
386 any2CharsC_ :: Parser ()
387 any2CharsC_ = anyCharC_ >> anyCharC_
389 manySatisfyC :: (Char -> Bool) -> Parser String
390 manySatisfyC = many . satisfyC
392 manySatisfyC_ :: (Char -> Bool) -> Parser ()
393 manySatisfyC_ = many_ . satisfyC
395 special :: Parser Token
397 manySatisfyC_ (\c -> isSpace c && c /= '\n')
402 manySatisfyC_ isSpace
403 sp <- keyArg (== '\n')
406 _ -> keyArg (const False)
408 keyArg :: (Char -> Bool) -> Parser Token
411 key <- keyword `message` "hsc keyword or '{' expected"
412 manySatisfyC_ (\c' -> isSpace c' && c' /= '\n' || eol c')
413 arg <- catchOutput_ (argument eol)
414 return (Special pos key arg)
416 keyword :: Parser String
418 c <- satisfyC (\c' -> isAlpha c' || c' == '_')
419 cs <- manySatisfyC (\c' -> isAlphaNum c' || c' == '_')
422 argument :: (Char -> Bool) -> Parser ()
427 c:_ | eol c -> do anyCharC_; argument eol
429 '\"':_ -> do anyCharC_; cString '\"'; argument eol
430 '\'':_ -> do anyCharC_; cString '\''; argument eol
431 '(':_ -> do anyCharC_; nested ')'; argument eol
433 '/':'*':_ -> do any2CharsC_; cComment; argument eol
435 any2CharsC_; manySatisfyC_ (/= '\n'); argument eol
436 '[':_ -> do anyCharC_; nested ']'; argument eol
438 '{':_ -> do anyCharC_; nested '}'; argument eol
440 _:_ -> do anyCharC_; argument eol
442 nested :: Char -> Parser ()
443 nested c = do argument (== '\n'); charC_ c
445 cComment :: Parser ()
450 '*':'/':_ -> do any2CharsC_
451 _:_ -> do anyCharC_; cComment
453 cString :: Char -> Parser ()
458 c:_ | c == quote -> anyCharC_
459 '\\':_:_ -> do any2CharsC_; cString quote
460 _:_ -> do anyCharC_; cString quote
462 ------------------------------------------------------------------------
463 -- Write the output files.
465 splitName :: String -> (String, String)
467 case break (== '/') name of
468 (file, []) -> ([], file)
469 (dir, sep:rest) -> (dir++sep:restDir, restFile)
471 (restDir, restFile) = splitName rest
473 splitExt :: String -> (String, String)
475 case break (== '.') name of
476 (base, []) -> (base, [])
477 (base, sepRest@(sep:rest))
478 | null restExt -> (base, sepRest)
479 | otherwise -> (base++sep:restBase, restExt)
481 (restBase, restExt) = splitExt rest
483 output :: [Flag] -> String -> [Token] -> IO ()
484 output flags name toks = do
486 (outName, outDir, outBase) <- case [f | Output f <- flags] of
487 [] -> if not (null ext) && last ext == 'c'
488 then return (dir++base++init ext, dir, base)
491 then return (dir++base++"_out.hs", dir, base)
492 else return (dir++base++".hs", dir, base)
494 (dir, file) = splitName name
495 (base, ext) = splitExt file
497 (dir, file) = splitName f
498 (base, _) = splitExt file
499 in return (f, dir, base)
500 _ -> onlyOne "output file"
502 let cProgName = outDir++outBase++"_hsc_make.c"
503 oProgName = outDir++outBase++"_hsc_make.o"
504 progName = outDir++outBase++"_hsc_make"
505 #if defined(mingw32_HOST_OS)
506 -- This is a real hack, but the quoting mechanism used for calling the C preprocesseor
507 -- via GHC has changed a few times, so this seems to be the only way... :-P * * *
510 outHFile = outBase++"_hsc.h"
511 outHName = outDir++outHFile
512 outCName = outDir++outBase++"_hsc.c"
514 beVerbose = any (\ x -> case x of { Verbose -> True; _ -> False}) flags
517 | null outDir = dosifyPath ("./" ++ progName)
518 | otherwise = progName
520 let specials = [(pos, key, arg) | Special pos key arg <- toks]
522 let needsC = any (\(_, key, _) -> key == "def") specials
525 let includeGuard = map fixChar outHName
527 fixChar c | isAlphaNum c = toUpper c
530 -- Try locating GHC..on Win32, look in the vicinity of hsc2hs.
531 -- Returns a native-format path
533 mb <- getExecDir "bin/hsc2hs.exe"
535 Nothing -> return def
537 let ghc_path = dosifyPath (x ++ "bin/ghc.exe")
538 flg <- doesFileExist ghc_path
543 -- On a Win32 installation we execute the hsc2hs binary directly,
544 -- with no --cc flags, so we'll call locateGhc here, which will
545 -- succeed, via getExecDir.
547 -- On a Unix installation, we'll run the wrapper script hsc2hs.sh
548 -- (called plain hsc2hs in the installed tree), which will pass
549 -- a suitable C compiler via --cc
551 -- The in-place installation always uses the wrapper script,
552 -- (called hsc2hs-inplace, generated from hsc2hs.sh)
553 compiler <- case [c | Compiler c <- flags] of
554 [] -> locateGhc "ghc"
556 _ -> onlyOne "compiler"
558 linker <- case [l | Linker l <- flags] of
559 [] -> locateGhc compiler
561 _ -> onlyOne "linker"
563 writeFile cProgName $
564 concatMap outFlagHeaderCProg flags++
565 concatMap outHeaderCProg specials++
566 "\nint main (int argc, char *argv [])\n{\n"++
567 outHeaderHs flags (if needsH then Just outHName else Nothing) specials++
568 outHsLine (SourcePos name 0)++
569 concatMap outTokenHs toks++
572 -- NOTE: hbc compiles "[() | NoCompile <- flags]" into wrong code,
573 -- so we use something slightly more complicated. :-P
574 when (any (\x -> case x of NoCompile -> True; _ -> False) flags) $
579 compilerStatus <- rawSystemL beVerbose compiler
581 ++ [f | CompFlag f <- flags]
586 case compilerStatus of
587 e@(ExitFailure _) -> exitWith e
591 linkerStatus <- rawSystemL beVerbose linker
592 ( [f | LinkFlag f <- flags]
598 e@(ExitFailure _) -> exitWith e
602 progStatus <- systemL beVerbose (execProgName++" >"++outName)
605 e@(ExitFailure _) -> exitWith e
608 when needsH $ writeFile outHName $
609 "#ifndef "++includeGuard++"\n" ++
610 "#define "++includeGuard++"\n" ++
611 "#if __GLASGOW_HASKELL__ && __GLASGOW_HASKELL__ < 409\n" ++
612 "#include <Rts.h>\n" ++
614 "#include <HsFFI.h>\n" ++
617 "#define HsChar int\n" ++
619 concatMap outFlagH flags++
620 concatMap outTokenH specials++
623 when needsC $ writeFile outCName $
624 "#include \""++outHFile++"\"\n"++
625 concatMap outTokenC specials
626 -- NB. outHFile not outHName; works better when processed
627 -- by gcc or mkdependC.
629 rawSystemL :: Bool -> String -> [String] -> IO ExitCode
630 rawSystemL flg prog args = do
631 when flg $ hPutStrLn stderr ("Executing: " ++ prog ++ concat (map (' ':) args))
634 systemL :: Bool -> String -> IO ExitCode
636 when flg (hPutStrLn stderr ("Executing: " ++ s))
639 onlyOne :: String -> IO a
640 onlyOne what = die ("Only one "++what++" may be specified\n")
642 outFlagHeaderCProg :: Flag -> String
643 outFlagHeaderCProg (Template t) = "#include \""++t++"\"\n"
644 outFlagHeaderCProg (Include f) = "#include "++f++"\n"
645 outFlagHeaderCProg (Define n Nothing) = "#define "++n++"\n"
646 outFlagHeaderCProg (Define n (Just v)) = "#define "++n++" "++v++"\n"
647 outFlagHeaderCProg _ = ""
649 outHeaderCProg :: (SourcePos, String, String) -> String
650 outHeaderCProg (pos, key, arg) = case key of
651 "include" -> outCLine pos++"#include "++arg++"\n"
652 "define" -> outCLine pos++"#define "++arg++"\n"
653 "undef" -> outCLine pos++"#undef "++arg++"\n"
655 's':'t':'r':'u':'c':'t':' ':_ -> outCLine pos++arg++"\n"
656 't':'y':'p':'e':'d':'e':'f':' ':_ -> outCLine pos++arg++"\n"
658 _ | conditional key -> outCLine pos++"#"++key++" "++arg++"\n"
659 "let" -> case break (== '=') arg of
661 (header, _:body) -> case break isSpace header of
664 "#define hsc_"++name++"("++dropWhile isSpace args++") " ++
665 "printf ("++joinLines body++");\n"
668 joinLines = concat . intersperse " \\\n" . lines
670 outHeaderHs :: [Flag] -> Maybe String -> [(SourcePos, String, String)] -> String
671 outHeaderHs flags inH toks =
673 "__GLASGOW_HASKELL__ && __GLASGOW_HASKELL__ < 409\n" ++
674 " printf (\"{-# OPTIONS -optc-D" ++
675 "__GLASGOW_HASKELL__=%d #-}\\n\", " ++
676 "__GLASGOW_HASKELL__);\n" ++
679 Nothing -> concatMap outFlag flags++concatMap outSpecial toks
680 Just f -> outOption ("-#include \""++f++"\"")
682 outFlag (Include f) = outOption ("-#include "++f)
683 outFlag (Define n Nothing) = outOption ("-optc-D"++n)
684 outFlag (Define n (Just v)) = outOption ("-optc-D"++n++"="++v)
686 outSpecial (pos, key, arg) = case key of
687 "include" -> outOption ("-#include "++arg)
688 "define" | goodForOptD arg -> outOption ("-optc-D"++toOptD arg)
690 _ | conditional key -> outCLine pos++"#"++key++" "++arg++"\n"
692 goodForOptD arg = case arg of
694 c:_ | isSpace c -> True
697 toOptD arg = case break isSpace arg of
699 (name, _:value) -> name++'=':dropWhile isSpace value
700 outOption s = " printf (\"{-# OPTIONS %s #-}\\n\", \""++
701 showCString s++"\");\n"
703 outTokenHs :: Token -> String
704 outTokenHs (Text pos txt) =
705 case break (== '\n') txt of
706 (allTxt, []) -> outText allTxt
708 outText (first++"\n")++
712 outText s = " fputs (\""++showCString s++"\", stdout);\n"
713 outTokenHs (Special pos key arg) =
719 _ | conditional key -> outCLine pos++"#"++key++" "++arg++"\n"
721 "enum" -> outCLine pos++outEnum arg
722 _ -> outCLine pos++" hsc_"++key++" ("++arg++");\n"
724 outEnum :: String -> String
726 case break (== ',') arg of
728 (t, _:afterT) -> case break (== ',') afterT of
731 enums (_:s) = case break (== ',') s of
733 this = case break (== '=') $ dropWhile isSpace enum of
735 " hsc_enum ("++t++", "++f++", " ++
736 "hsc_haskellize (\""++name++"\"), "++
739 " hsc_enum ("++t++", "++f++", " ++
740 "printf (\"%s\", \""++hsName++"\"), "++
745 outFlagH :: Flag -> String
746 outFlagH (Include f) = "#include "++f++"\n"
747 outFlagH (Define n Nothing) = "#define "++n++"\n"
748 outFlagH (Define n (Just v)) = "#define "++n++" "++v++"\n"
751 outTokenH :: (SourcePos, String, String) -> String
752 outTokenH (pos, key, arg) =
754 "include" -> outCLine pos++"#include "++arg++"\n"
755 "define" -> outCLine pos++"#define " ++arg++"\n"
756 "undef" -> outCLine pos++"#undef " ++arg++"\n"
757 "def" -> outCLine pos++case arg of
758 's':'t':'r':'u':'c':'t':' ':_ -> arg++"\n"
759 't':'y':'p':'e':'d':'e':'f':' ':_ -> arg++"\n"
760 'i':'n':'l':'i':'n':'e':' ':_ ->
761 "#ifdef __GNUC__\n" ++
765 _ -> "extern "++header++";\n"
766 where header = takeWhile (\c -> c /= '{' && c /= '=') arg
767 _ | conditional key -> outCLine pos++"#"++key++" "++arg++"\n"
770 outTokenC :: (SourcePos, String, String) -> String
771 outTokenC (pos, key, arg) =
774 's':'t':'r':'u':'c':'t':' ':_ -> ""
775 't':'y':'p':'e':'d':'e':'f':' ':_ -> ""
776 'i':'n':'l':'i':'n':'e':' ':arg' ->
777 case span (\c -> c /= '{' && c /= '=') arg' of
780 "#ifndef __GNUC__\n" ++
784 "\n#ifndef __GNUC__\n" ++
789 _ -> outCLine pos++arg++"\n"
790 _ | conditional key -> outCLine pos++"#"++key++" "++arg++"\n"
793 conditional :: String -> Bool
794 conditional "if" = True
795 conditional "ifdef" = True
796 conditional "ifndef" = True
797 conditional "elif" = True
798 conditional "else" = True
799 conditional "endif" = True
800 conditional "error" = True
801 conditional "warning" = True
802 conditional _ = False
804 outCLine :: SourcePos -> String
805 outCLine (SourcePos name line) =
806 "#line "++show line++" \""++showCString (snd (splitName name))++"\"\n"
808 outHsLine :: SourcePos -> String
809 outHsLine (SourcePos name line) =
810 " hsc_line ("++show (line + 1)++", \""++
811 showCString (snd (splitName name))++"\");\n"
813 showCString :: String -> String
814 showCString = concatMap showCChar
816 showCChar '\"' = "\\\""
817 showCChar '\'' = "\\\'"
818 showCChar '?' = "\\?"
819 showCChar '\\' = "\\\\"
820 showCChar c | c >= ' ' && c <= '~' = [c]
821 showCChar '\a' = "\\a"
822 showCChar '\b' = "\\b"
823 showCChar '\f' = "\\f"
824 showCChar '\n' = "\\n\"\n \""
825 showCChar '\r' = "\\r"
826 showCChar '\t' = "\\t"
827 showCChar '\v' = "\\v"
829 intToDigit (ord c `quot` 64),
830 intToDigit (ord c `quot` 8 `mod` 8),
831 intToDigit (ord c `mod` 8)]
835 -----------------------------------------
836 -- Cut and pasted from ghc/compiler/SysTools
837 -- Convert paths foo/baz to foo\baz on Windows
839 dosifyPath :: String -> String
840 #if defined(mingw32_HOST_OS)
841 dosifyPath xs = subst '/' '\\' xs
843 unDosifyPath :: String -> String
844 unDosifyPath xs = subst '\\' '/' xs
846 subst :: Eq a => a -> a -> [a] -> [a]
847 subst a b ls = map (\ x -> if x == a then b else x) ls
849 getExecDir :: String -> IO (Maybe String)
850 -- (getExecDir cmd) returns the directory in which the current
851 -- executable, which should be called 'cmd', is running
852 -- So if the full path is /a/b/c/d/e, and you pass "d/e" as cmd,
853 -- you'll get "/a/b/c" back as the result
855 = allocaArray len $ \buf -> do
856 ret <- getModuleFileName nullPtr buf len
857 if ret == 0 then return Nothing
858 else do s <- peekCString buf
859 return (Just (reverse (drop (length cmd)
860 (reverse (unDosifyPath s)))))
862 len = 2048::Int -- Plenty, PATH_MAX is 512 under Win32.
864 foreign import stdcall unsafe "GetModuleFileNameA"
865 getModuleFileName :: Ptr () -> CString -> Int -> IO Int32
870 getExecDir :: String -> IO (Maybe String)
871 getExecDir _ = return Nothing