9f202fd654d2044003e35fa495dc62217dd26d34
[ghc-hetmet.git] / utils / hsc2hs / Main.hs
1 {-# OPTIONS -fffi -cpp #-}
2
3 ------------------------------------------------------------------------
4 -- Program for converting .hsc files to .hs files, by converting the
5 -- file into a C program which is run to generate the Haskell source.
6 -- Certain items known only to the C compiler can then be used in
7 -- the Haskell module; for example #defined constants, byte offsets
8 -- within structures, etc.
9 --
10 -- See the documentation in the Users' Guide for more details.
11
12 #if defined(__GLASGOW_HASKELL__) && !defined(BUILD_NHC)
13 #include "../../includes/ghcconfig.h"
14 #endif
15
16 #if __GLASGOW_HASKELL__ >= 504 || __NHC__ >= 114 || __HUGS__
17 import System.Console.GetOpt
18 #else
19 import GetOpt
20 #endif
21
22 import System        (getProgName, getArgs, ExitCode(..), exitWith)
23 import Directory     (removeFile,doesFileExist)
24 import Monad         (MonadPlus(..), liftM, liftM2, when)
25 import Char          (isAlpha, isAlphaNum, isSpace, isDigit, toUpper, intToDigit, ord)
26 import List          (intersperse, isSuffixOf)
27 import IO            (hPutStr, hPutStrLn, stderr)
28
29 #if defined(mingw32_HOST_OS) && !__HUGS__
30 import Foreign
31 #if __GLASGOW_HASKELL__ >= 504 || __NHC__ >= 114
32 import Foreign.C.String
33 #else
34 import CString
35 #endif
36 #endif
37
38 #if __GLASGOW_HASKELL__ >= 604
39 import System.Process           ( runProcess, waitForProcess )
40 import System.IO                ( openFile, IOMode(..), hClose )
41 #define HAVE_runProcess
42 #endif
43
44 #if defined(__GLASGOW_HASKELL__) && !defined(BUILD_NHC)
45 import Compat.RawSystem         ( rawSystem )
46 #define HAVE_rawSystem
47 #elif __HUGS__ || __NHC__ >= 117
48 import System.Cmd               ( rawSystem )
49 #define HAVE_rawSystem
50 #endif
51
52 #if !defined(HAVE_runProcess) || !defined(HAVE_rawSystem)
53 -- we need system
54 #if __HUGS__ || __NHC__ >= 117 || __GLASGOW_HASKELL__ >= 600
55 import System.Cmd               ( system )
56 #else
57 import System                   ( system )
58 #endif
59 #endif
60
61 version :: String
62 version = "hsc2hs version 0.66\n"
63
64 data Flag
65     = Help
66     | Version
67     | Template  String
68     | Compiler  String
69     | Linker    String
70     | CompFlag  String
71     | LinkFlag  String
72     | NoCompile
73     | Include   String
74     | Define    String (Maybe String)
75     | Output    String
76     | Verbose
77
78 template_flag :: Flag -> Bool
79 template_flag (Template _) = True
80 template_flag _            = False
81
82 include :: String -> Flag
83 include s@('\"':_) = Include s
84 include s@('<' :_) = Include s
85 include s          = Include ("\""++s++"\"")
86
87 define :: String -> Flag
88 define s = case break (== '=') s of
89     (name, [])      -> Define name Nothing
90     (name, _:value) -> Define name (Just value)
91
92 options :: [OptDescr Flag]
93 options = [
94     Option ['o'] ["output"]     (ReqArg Output     "FILE")
95         "name of main output file",
96     Option ['t'] ["template"]   (ReqArg Template   "FILE")
97         "template file",
98     Option ['c'] ["cc"]         (ReqArg Compiler   "PROG")
99         "C compiler to use",
100     Option ['l'] ["ld"]         (ReqArg Linker     "PROG")
101         "linker to use",
102     Option ['C'] ["cflag"]      (ReqArg CompFlag   "FLAG")
103         "flag to pass to the C compiler",
104     Option ['I'] []             (ReqArg (CompFlag . ("-I"++)) "DIR")
105         "passed to the C compiler",
106     Option ['L'] ["lflag"]      (ReqArg LinkFlag   "FLAG")
107         "flag to pass to the linker",
108     Option ['i'] ["include"]    (ReqArg include    "FILE")
109         "as if placed in the source",
110     Option ['D'] ["define"]     (ReqArg define "NAME[=VALUE]")
111         "as if placed in the source",
112     Option []    ["no-compile"] (NoArg  NoCompile)
113         "stop after writing *_hsc_make.c",
114     Option ['v'] ["verbose"]    (NoArg  Verbose)
115         "dump commands to stderr",
116     Option ['?'] ["help"]       (NoArg  Help)
117         "display this help and exit",
118     Option ['V'] ["version"]    (NoArg  Version)
119         "output version information and exit" ]
120
121 main :: IO ()
122 main = do
123     prog <- getProgramName
124     let header = "Usage: "++prog++" [OPTIONS] INPUT.hsc [...]\n"
125     args <- getArgs
126     let (flags, files, errs) = getOpt Permute options args
127
128         -- If there is no Template flag explicitly specified, try
129         -- to find one by looking near the executable.  This only
130         -- works on Win32 or Hugs (getExecDir). On Unix, there's a wrapper
131         -- script which specifies an explicit template flag.
132     flags_w_tpl <- if any template_flag flags then
133                         return flags
134                    else
135 #ifdef __HUGS__
136                         do mb_path <- getExecDir "/Main.hs"
137 #else
138                         do mb_path <- getExecDir "/bin/hsc2hs.exe"
139 #endif
140                            add_opt <-
141                             case mb_path of
142                               Nothing   -> return id
143                               Just path -> do
144                                 let templ = path ++ "/template-hsc.h"
145                                 flg <- doesFileExist templ
146                                 if flg
147                                  then return ((Template templ):)
148                                  else return id
149                            return (add_opt flags)
150     case (files, errs) of
151         (_, _)
152             | any isHelp    flags_w_tpl -> bye (usageInfo header options)
153             | any isVersion flags_w_tpl -> bye version
154             where
155             isHelp    Help    = True; isHelp    _ = False
156             isVersion Version = True; isVersion _ = False
157         ((_:_), []) -> mapM_ (processFile flags_w_tpl) files
158         (_,     _ ) -> die (concat errs ++ usageInfo header options)
159
160 getProgramName :: IO String
161 getProgramName = liftM (`withoutSuffix` "-bin") getProgName
162    where str `withoutSuffix` suff
163             | suff `isSuffixOf` str = take (length str - length suff) str
164             | otherwise             = str
165
166 bye :: String -> IO a
167 bye s = putStr s >> exitWith ExitSuccess
168
169 die :: String -> IO a
170 die s = hPutStr stderr s >> exitWith (ExitFailure 1)
171
172 processFile :: [Flag] -> String -> IO ()
173 processFile flags name
174   = do let file_name = dosifyPath name
175        s <- readFile file_name
176        case parser of
177            Parser p -> case p (SourcePos file_name 1) s of
178                Success _ _ _ toks -> output flags file_name toks
179                Failure (SourcePos name' line) msg ->
180                    die (name'++":"++show line++": "++msg++"\n")
181
182 ------------------------------------------------------------------------
183 -- A deterministic parser which remembers the text which has been parsed.
184
185 newtype Parser a = Parser (SourcePos -> String -> ParseResult a)
186
187 data ParseResult a = Success !SourcePos String String a
188                    | Failure !SourcePos String
189
190 data SourcePos = SourcePos String !Int
191
192 updatePos :: SourcePos -> Char -> SourcePos
193 updatePos pos@(SourcePos name line) ch = case ch of
194     '\n' -> SourcePos name (line + 1)
195     _    -> pos
196
197 instance Monad Parser where
198     return a = Parser $ \pos s -> Success pos [] s a
199     Parser m >>= k =
200         Parser $ \pos s -> case m pos s of
201             Success pos' out1 s' a -> case k a of
202                 Parser k' -> case k' pos' s' of
203                     Success pos'' out2 imp'' b ->
204                         Success pos'' (out1++out2) imp'' b
205                     Failure pos'' msg -> Failure pos'' msg
206             Failure pos' msg -> Failure pos' msg
207     fail msg = Parser $ \pos _ -> Failure pos msg
208
209 instance MonadPlus Parser where
210     mzero                     = fail "mzero"
211     Parser m `mplus` Parser n =
212         Parser $ \pos s -> case m pos s of
213             success@(Success _ _ _ _) -> success
214             Failure _ _               -> n pos s
215
216 getPos :: Parser SourcePos
217 getPos = Parser $ \pos s -> Success pos [] s pos
218
219 setPos :: SourcePos -> Parser ()
220 setPos pos = Parser $ \_ s -> Success pos [] s ()
221
222 message :: Parser a -> String -> Parser a
223 Parser m `message` msg =
224     Parser $ \pos s -> case m pos s of
225         success@(Success _ _ _ _) -> success
226         Failure pos' _            -> Failure pos' msg
227
228 catchOutput_ :: Parser a -> Parser String
229 catchOutput_ (Parser m) =
230     Parser $ \pos s -> case m pos s of
231         Success pos' out s' _ -> Success pos' [] s' out
232         Failure pos' msg      -> Failure pos' msg
233
234 fakeOutput :: Parser a -> String -> Parser a
235 Parser m `fakeOutput` out =
236     Parser $ \pos s -> case m pos s of
237         Success pos' _ s' a -> Success pos' out s' a
238         Failure pos' msg    -> Failure pos' msg
239
240 lookAhead :: Parser String
241 lookAhead = Parser $ \pos s -> Success pos [] s s
242
243 satisfy :: (Char -> Bool) -> Parser Char
244 satisfy p =
245     Parser $ \pos s -> case s of
246         c:cs | p c -> Success (updatePos pos c) [c] cs c
247         _          -> Failure pos "Bad character"
248
249 char_ :: Char -> Parser ()
250 char_ c = do
251     satisfy (== c) `message` (show c++" expected")
252     return ()
253
254 anyChar_ :: Parser ()
255 anyChar_ = do
256     satisfy (const True) `message` "Unexpected end of file"
257     return ()
258
259 any2Chars_ :: Parser ()
260 any2Chars_ = anyChar_ >> anyChar_
261
262 many :: Parser a -> Parser [a]
263 many p = many1 p `mplus` return []
264
265 many1 :: Parser a -> Parser [a]
266 many1 p = liftM2 (:) p (many p)
267
268 many_ :: Parser a -> Parser ()
269 many_ p = many1_ p `mplus` return ()
270
271 many1_ :: Parser a -> Parser ()
272 many1_ p = p >> many_ p
273
274 manySatisfy, manySatisfy1 :: (Char -> Bool) -> Parser String
275 manySatisfy  = many  . satisfy
276 manySatisfy1 = many1 . satisfy
277
278 manySatisfy_, manySatisfy1_ :: (Char -> Bool) -> Parser ()
279 manySatisfy_  = many_  . satisfy
280 manySatisfy1_ = many1_ . satisfy
281
282 ------------------------------------------------------------------------
283 -- Parser of hsc syntax.
284
285 data Token
286     = Text    SourcePos String
287     | Special SourcePos String String
288
289 parser :: Parser [Token]
290 parser = do
291     pos <- getPos
292     t <- catchOutput_ text
293     s <- lookAhead
294     rest <- case s of
295         []  -> return []
296         _:_ -> liftM2 (:) (special `fakeOutput` []) parser
297     return (if null t then rest else Text pos t : rest)
298
299 text :: Parser ()
300 text = do
301     s <- lookAhead
302     case s of
303         []        -> return ()
304         c:_ | isAlpha c || c == '_' -> do
305             anyChar_
306             manySatisfy_ (\c' -> isAlphaNum c' || c' == '_' || c' == '\'')
307             text
308         c:_ | isHsSymbol c -> do
309             symb <- catchOutput_ (manySatisfy_ isHsSymbol)
310             case symb of
311                 "#" -> return ()
312                 '-':'-':symb' | all (== '-') symb' -> do
313                     return () `fakeOutput` symb
314                     manySatisfy_ (/= '\n')
315                     text
316                 _ -> do
317                     return () `fakeOutput` unescapeHashes symb
318                     text
319         '\"':_    -> do anyChar_; hsString '\"'; text
320         '\'':_    -> do anyChar_; hsString '\''; text
321         '{':'-':_ -> do any2Chars_; linePragma `mplus` hsComment; text
322         _:_       -> do anyChar_; text
323
324 hsString :: Char -> Parser ()
325 hsString quote = do
326     s <- lookAhead
327     case s of
328         []               -> return ()
329         c:_ | c == quote -> anyChar_
330         '\\':c:_
331             | isSpace c  -> do
332                 anyChar_
333                 manySatisfy_ isSpace
334                 char_ '\\' `mplus` return ()
335                 hsString quote
336             | otherwise  -> do any2Chars_; hsString quote
337         _:_              -> do anyChar_; hsString quote
338
339 hsComment :: Parser ()
340 hsComment = do
341     s <- lookAhead
342     case s of
343         []        -> return ()
344         '-':'}':_ -> any2Chars_
345         '{':'-':_ -> do any2Chars_; hsComment; hsComment
346         _:_       -> do anyChar_; hsComment
347
348 linePragma :: Parser ()
349 linePragma = do
350     char_ '#'
351     manySatisfy_ isSpace
352     satisfy (\c -> c == 'L' || c == 'l')
353     satisfy (\c -> c == 'I' || c == 'i')
354     satisfy (\c -> c == 'N' || c == 'n')
355     satisfy (\c -> c == 'E' || c == 'e')
356     manySatisfy1_ isSpace
357     line <- liftM read $ manySatisfy1 isDigit
358     manySatisfy1_ isSpace
359     char_ '\"'
360     name <- manySatisfy (/= '\"')
361     char_ '\"'
362     manySatisfy_ isSpace
363     char_ '#'
364     char_ '-'
365     char_ '}'
366     setPos (SourcePos name (line - 1))
367
368 isHsSymbol :: Char -> Bool
369 isHsSymbol '!' = True; isHsSymbol '#' = True; isHsSymbol '$'  = True
370 isHsSymbol '%' = True; isHsSymbol '&' = True; isHsSymbol '*'  = True
371 isHsSymbol '+' = True; isHsSymbol '.' = True; isHsSymbol '/'  = True
372 isHsSymbol '<' = True; isHsSymbol '=' = True; isHsSymbol '>'  = True
373 isHsSymbol '?' = True; isHsSymbol '@' = True; isHsSymbol '\\' = True
374 isHsSymbol '^' = True; isHsSymbol '|' = True; isHsSymbol '-'  = True
375 isHsSymbol '~' = True
376 isHsSymbol _   = False
377
378 unescapeHashes :: String -> String
379 unescapeHashes []          = []
380 unescapeHashes ('#':'#':s) = '#' : unescapeHashes s
381 unescapeHashes (c:s)       = c   : unescapeHashes s
382
383 lookAheadC :: Parser String
384 lookAheadC = liftM joinLines lookAhead
385     where
386     joinLines []            = []
387     joinLines ('\\':'\n':s) = joinLines s
388     joinLines (c:s)         = c : joinLines s
389
390 satisfyC :: (Char -> Bool) -> Parser Char
391 satisfyC p = do
392     s <- lookAhead
393     case s of
394         '\\':'\n':_ -> do any2Chars_ `fakeOutput` []; satisfyC p
395         _           -> satisfy p
396
397 charC_ :: Char -> Parser ()
398 charC_ c = do
399     satisfyC (== c) `message` (show c++" expected")
400     return ()
401
402 anyCharC_ :: Parser ()
403 anyCharC_ = do
404     satisfyC (const True) `message` "Unexpected end of file"
405     return ()
406
407 any2CharsC_ :: Parser ()
408 any2CharsC_ = anyCharC_ >> anyCharC_
409
410 manySatisfyC :: (Char -> Bool) -> Parser String
411 manySatisfyC = many . satisfyC
412
413 manySatisfyC_ :: (Char -> Bool) -> Parser ()
414 manySatisfyC_ = many_ . satisfyC
415
416 special :: Parser Token
417 special = do
418     manySatisfyC_ (\c -> isSpace c && c /= '\n')
419     s <- lookAheadC
420     case s of
421         '{':_ -> do
422             anyCharC_
423             manySatisfyC_ isSpace
424             sp <- keyArg (== '\n')
425             charC_ '}'
426             return sp
427         _ -> keyArg (const False)
428
429 keyArg :: (Char -> Bool) -> Parser Token
430 keyArg eol = do
431     pos <- getPos
432     key <- keyword `message` "hsc keyword or '{' expected"
433     manySatisfyC_ (\c' -> isSpace c' && c' /= '\n' || eol c')
434     arg <- catchOutput_ (argument eol)
435     return (Special pos key arg)
436
437 keyword :: Parser String
438 keyword = do
439     c  <- satisfyC (\c' -> isAlpha c' || c' == '_')
440     cs <- manySatisfyC (\c' -> isAlphaNum c' || c' == '_')
441     return (c:cs)
442
443 argument :: (Char -> Bool) -> Parser ()
444 argument eol = do
445     s <- lookAheadC
446     case s of
447         []          -> return ()
448         c:_ | eol c -> do anyCharC_;               argument eol
449         '\n':_      -> return ()
450         '\"':_      -> do anyCharC_; cString '\"'; argument eol
451         '\'':_      -> do anyCharC_; cString '\''; argument eol
452         '(':_       -> do anyCharC_; nested ')';   argument eol
453         ')':_       -> return ()
454         '/':'*':_   -> do any2CharsC_; cComment;   argument eol
455         '/':'/':_   -> do
456             any2CharsC_; manySatisfyC_ (/= '\n');  argument eol
457         '[':_       -> do anyCharC_; nested ']';   argument eol
458         ']':_       -> return ()
459         '{':_       -> do anyCharC_; nested '}';   argument eol
460         '}':_       -> return ()
461         _:_         -> do anyCharC_;               argument eol
462
463 nested :: Char -> Parser ()
464 nested c = do argument (== '\n'); charC_ c
465
466 cComment :: Parser ()
467 cComment = do
468     s <- lookAheadC
469     case s of
470         []        -> return ()
471         '*':'/':_ -> do any2CharsC_
472         _:_       -> do anyCharC_; cComment
473
474 cString :: Char -> Parser ()
475 cString quote = do
476     s <- lookAheadC
477     case s of
478         []               -> return ()
479         c:_ | c == quote -> anyCharC_
480         '\\':_:_         -> do any2CharsC_; cString quote
481         _:_              -> do anyCharC_; cString quote
482
483 ------------------------------------------------------------------------
484 -- Write the output files.
485
486 splitName :: String -> (String, String)
487 splitName name =
488     case break (== '/') name of
489         (file, [])       -> ([], file)
490         (dir,  sep:rest) -> (dir++sep:restDir, restFile)
491             where
492             (restDir, restFile) = splitName rest
493
494 splitExt :: String -> (String, String)
495 splitExt name =
496     case break (== '.') name of
497         (base, [])         -> (base, [])
498         (base, sepRest@(sep:rest))
499             | null restExt -> (base,               sepRest)
500             | otherwise    -> (base++sep:restBase, restExt)
501             where
502             (restBase, restExt) = splitExt rest
503
504 output :: [Flag] -> String -> [Token] -> IO ()
505 output flags name toks = do
506
507     (outName, outDir, outBase) <- case [f | Output f <- flags] of
508         [] -> if not (null ext) && last ext == 'c'
509                  then return (dir++base++init ext,  dir, base)
510                  else
511                     if ext == ".hs"
512                        then return (dir++base++"_out.hs", dir, base)
513                        else return (dir++base++".hs",     dir, base)
514               where
515                (dir,  file) = splitName name
516                (base, ext)  = splitExt  file
517         [f] -> let
518             (dir,  file) = splitName f
519             (base, _)    = splitExt file
520             in return (f, dir, base)
521         _ -> onlyOne "output file"
522
523     let cProgName    = outDir++outBase++"_hsc_make.c"
524         oProgName    = outDir++outBase++"_hsc_make.o"
525         progName     = outDir++outBase++"_hsc_make"
526 #if defined(mingw32_HOST_OS) || defined(__CYGWIN32__)
527 -- This is a real hack, but the quoting mechanism used for calling the C preprocesseor
528 -- via GHC has changed a few times, so this seems to be the only way...  :-P * * *
529                           ++ ".exe"
530 #endif
531         outHFile     = outBase++"_hsc.h"
532         outHName     = outDir++outHFile
533         outCName     = outDir++outBase++"_hsc.c"
534
535         beVerbose    = any (\ x -> case x of { Verbose -> True; _ -> False}) flags
536
537     let execProgName
538             | null outDir = dosifyPath ("./" ++ progName)
539             | otherwise   = progName
540
541     let specials = [(pos, key, arg) | Special pos key arg <- toks]
542
543     let needsC = any (\(_, key, _) -> key == "def") specials
544         needsH = needsC
545
546     let includeGuard = map fixChar outHName
547             where
548             fixChar c | isAlphaNum c = toUpper c
549                       | otherwise    = '_'
550
551 #ifdef __HUGS__
552     compiler <- case [c | Compiler c <- flags] of
553         []  -> return "gcc"
554         [c] -> return c
555         _   -> onlyOne "compiler"
556
557     linker <- case [l | Linker l <- flags] of
558         []  -> return compiler
559         [l] -> return l
560         _   -> onlyOne "linker"
561 #else
562         -- Try locating GHC..on Win32, look in the vicinity of hsc2hs.
563         -- Returns a native-format path
564         locateGhc def = do
565             mb <- getExecDir "bin/hsc2hs.exe"
566             case mb of
567               Nothing -> return def
568               Just x  -> do
569                  let ghc_path = dosifyPath (x ++ "bin/ghc.exe")
570                  flg <- doesFileExist ghc_path
571                  if flg
572                   then return ghc_path
573                   else return def
574
575         -- On a Win32 installation we execute the hsc2hs binary directly,
576         -- with no --cc flags, so we'll call locateGhc here, which will
577         -- succeed, via getExecDir.
578         --
579         -- On a Unix installation, we'll run the wrapper script hsc2hs.sh
580         -- (called plain hsc2hs in the installed tree), which will pass
581         -- a suitable C compiler via --cc
582         --
583         -- The in-place installation always uses the wrapper script,
584         -- (called hsc2hs-inplace, generated from hsc2hs.sh)
585     compiler <- case [c | Compiler c <- flags] of
586         []  -> locateGhc "ghc"
587         [c] -> return c
588         _   -> onlyOne "compiler"
589
590     linker <- case [l | Linker l <- flags] of
591         []  -> locateGhc compiler
592         [l] -> return l
593         _   -> onlyOne "linker"
594 #endif
595
596     writeFile cProgName $
597         concatMap outFlagHeaderCProg flags++
598         concatMap outHeaderCProg specials++
599         "\nint main (int argc, char *argv [])\n{\n"++
600         outHeaderHs flags (if needsH then Just outHName else Nothing) specials++
601         outHsLine (SourcePos name 0)++
602         concatMap outTokenHs toks++
603         "    return 0;\n}\n"
604
605     -- NOTE: hbc compiles "[() | NoCompile <- flags]" into wrong code,
606     -- so we use something slightly more complicated.   :-P
607     when (any (\x -> case x of NoCompile -> True; _ -> False) flags) $
608        exitWith ExitSuccess
609
610     rawSystemL ("compiling " ++ cProgName) beVerbose compiler
611         (  ["-c"]
612         ++ [f | CompFlag f <- flags]
613         ++ [cProgName]
614         ++ ["-o", oProgName]
615         )
616     removeFile cProgName
617
618     rawSystemL ("linking " ++ oProgName) beVerbose linker
619         (  [f | LinkFlag f <- flags]
620         ++ [oProgName]
621         ++ ["-o", progName]
622         )
623     removeFile oProgName
624
625     rawSystemWithStdOutL ("running " ++ execProgName) beVerbose execProgName [] outName
626     removeFile progName
627
628     when needsH $ writeFile outHName $
629         "#ifndef "++includeGuard++"\n" ++
630         "#define "++includeGuard++"\n" ++
631         "#if __GLASGOW_HASKELL__ && __GLASGOW_HASKELL__ < 409\n" ++
632         "#include <Rts.h>\n" ++
633         "#endif\n" ++
634         "#include <HsFFI.h>\n" ++
635         "#if __NHC__\n" ++
636         "#undef HsChar\n" ++
637         "#define HsChar int\n" ++
638         "#endif\n" ++
639         concatMap outFlagH flags++
640         concatMap outTokenH specials++
641         "#endif\n"
642
643     when needsC $ writeFile outCName $
644         "#include \""++outHFile++"\"\n"++
645         concatMap outTokenC specials
646         -- NB. outHFile not outHName; works better when processed
647         -- by gcc or mkdependC.
648
649 rawSystemL :: String -> Bool -> FilePath -> [String] -> IO ()
650 rawSystemL action flg prog args = do
651   let cmdLine = prog++" "++unwords args
652   when flg $ hPutStrLn stderr ("Executing: " ++ cmdLine)
653 #ifndef HAVE_rawSystem
654   exitStatus <- system cmdLine
655 #else
656   exitStatus <- rawSystem prog args
657 #endif
658   case exitStatus of
659     ExitFailure _ -> die $ action ++ " failed\ncommand was: " ++ cmdLine ++ "\n"
660     _             -> return ()
661
662 rawSystemWithStdOutL :: String -> Bool -> FilePath -> [String] -> FilePath -> IO ()
663 rawSystemWithStdOutL action flg prog args outFile = do
664   let cmdLine = prog++" "++unwords args++" >"++outFile
665   when flg (hPutStrLn stderr ("Executing: " ++ cmdLine))
666 #ifndef HAVE_runProcess
667   exitStatus <- system cmdLine
668 #else
669   hOut <- openFile outFile WriteMode
670   process <- runProcess prog args Nothing Nothing Nothing (Just hOut) Nothing
671   exitStatus <- waitForProcess process
672   hClose hOut
673 #endif
674   case exitStatus of
675     ExitFailure _ -> die $ action ++ " failed\ncommand was: " ++ cmdLine ++ "\n"
676     _             -> return ()
677
678 onlyOne :: String -> IO a
679 onlyOne what = die ("Only one "++what++" may be specified\n")
680
681 outFlagHeaderCProg :: Flag -> String
682 outFlagHeaderCProg (Template t)          = "#include \""++t++"\"\n"
683 outFlagHeaderCProg (Include  f)          = "#include "++f++"\n"
684 outFlagHeaderCProg (Define   n Nothing)  = "#define "++n++" 1\n"
685 outFlagHeaderCProg (Define   n (Just v)) = "#define "++n++" "++v++"\n"
686 outFlagHeaderCProg _                     = ""
687
688 outHeaderCProg :: (SourcePos, String, String) -> String
689 outHeaderCProg (pos, key, arg) = case key of
690     "include"           -> outCLine pos++"#include "++arg++"\n"
691     "define"            -> outCLine pos++"#define "++arg++"\n"
692     "undef"             -> outCLine pos++"#undef "++arg++"\n"
693     "def"               -> case arg of
694         's':'t':'r':'u':'c':'t':' ':_ -> outCLine pos++arg++"\n"
695         't':'y':'p':'e':'d':'e':'f':' ':_ -> outCLine pos++arg++"\n"
696         _ -> ""
697     _ | conditional key -> outCLine pos++"#"++key++" "++arg++"\n"
698     "let"               -> case break (== '=') arg of
699         (_,      "")     -> ""
700         (header, _:body) -> case break isSpace header of
701             (name, args) ->
702                 outCLine pos++
703                 "#define hsc_"++name++"("++dropWhile isSpace args++") " ++
704                 "printf ("++joinLines body++");\n"
705     _ -> ""
706    where
707     joinLines = concat . intersperse " \\\n" . lines
708
709 outHeaderHs :: [Flag] -> Maybe String -> [(SourcePos, String, String)] -> String
710 outHeaderHs flags inH toks =
711     "#if " ++
712     "__GLASGOW_HASKELL__ && __GLASGOW_HASKELL__ < 409\n" ++
713     "    printf (\"{-# OPTIONS -optc-D" ++
714     "__GLASGOW_HASKELL__=%d #-}\\n\", " ++
715     "__GLASGOW_HASKELL__);\n" ++
716     "#endif\n"++
717     case inH of
718         Nothing -> concatMap outFlag flags++concatMap outSpecial toks
719         Just f  -> outInclude ("\""++f++"\"")
720     where
721     outFlag (Include f)          = outInclude f
722     outFlag (Define  n Nothing)  = outOption ("-optc-D"++n)
723     outFlag (Define  n (Just v)) = outOption ("-optc-D"++n++"="++v)
724     outFlag _                    = ""
725     outSpecial (pos, key, arg) = case key of
726         "include"                  -> outInclude arg
727         "define" | goodForOptD arg -> outOption ("-optc-D"++toOptD arg)
728                  | otherwise       -> ""
729         _ | conditional key        -> outCLine pos++"#"++key++" "++arg++"\n"
730         _                          -> ""
731     goodForOptD arg = case arg of
732         ""              -> True
733         c:_ | isSpace c -> True
734         '(':_           -> False
735         _:s             -> goodForOptD s
736     toOptD arg = case break isSpace arg of
737         (name, "")      -> name
738         (name, _:value) -> name++'=':dropWhile isSpace value
739     outOption s =
740         "#if __GLASGOW_HASKELL__ && __GLASGOW_HASKELL__ < 603\n" ++
741         "    printf (\"{-# OPTIONS %s #-}\\n\", \""++
742                   showCString s++"\");\n"++
743         "#else\n"++
744         "    printf (\"{-# OPTIONS_GHC %s #-}\\n\", \""++
745                   showCString s++"\");\n"++
746         "#endif\n"
747     outInclude s =
748         "#if __GLASGOW_HASKELL__ && __GLASGOW_HASKELL__ < 603\n" ++
749         "    printf (\"{-# OPTIONS -#include %s #-}\\n\", \""++
750                   showCString s++"\");\n"++
751         "#else\n"++
752         "    printf (\"{-# INCLUDE %s #-}\\n\", \""++
753                   showCString s++"\");\n"++
754         "#endif\n"
755
756 outTokenHs :: Token -> String
757 outTokenHs (Text pos txt) =
758     case break (== '\n') txt of
759         (allTxt, [])       -> outText allTxt
760         (first, _:rest) ->
761             outText (first++"\n")++
762             outHsLine pos++
763             outText rest
764     where
765     outText s = "    fputs (\""++showCString s++"\", stdout);\n"
766 outTokenHs (Special pos key arg) =
767     case key of
768         "include"           -> ""
769         "define"            -> ""
770         "undef"             -> ""
771         "def"               -> ""
772         _ | conditional key -> outCLine pos++"#"++key++" "++arg++"\n"
773         "let"               -> ""
774         "enum"              -> outCLine pos++outEnum arg
775         _                   -> outCLine pos++"    hsc_"++key++" ("++arg++");\n"
776
777 outEnum :: String -> String
778 outEnum arg =
779     case break (== ',') arg of
780         (_, [])        -> ""
781         (t, _:afterT) -> case break (== ',') afterT of
782             (f, afterF) -> let
783                 enums []    = ""
784                 enums (_:s) = case break (== ',') s of
785                     (enum, rest) -> let
786                         this = case break (== '=') $ dropWhile isSpace enum of
787                             (name, []) ->
788                                 "    hsc_enum ("++t++", "++f++", " ++
789                                 "hsc_haskellize (\""++name++"\"), "++
790                                 name++");\n"
791                             (hsName, _:cName) ->
792                                 "    hsc_enum ("++t++", "++f++", " ++
793                                 "printf (\"%s\", \""++hsName++"\"), "++
794                                 cName++");\n"
795                         in this++enums rest
796                 in enums afterF
797
798 outFlagH :: Flag -> String
799 outFlagH (Include  f)          = "#include "++f++"\n"
800 outFlagH (Define   n Nothing)  = "#define "++n++" 1\n"
801 outFlagH (Define   n (Just v)) = "#define "++n++" "++v++"\n"
802 outFlagH _                     = ""
803
804 outTokenH :: (SourcePos, String, String) -> String
805 outTokenH (pos, key, arg) =
806     case key of
807         "include" -> outCLine pos++"#include "++arg++"\n"
808         "define"  -> outCLine pos++"#define " ++arg++"\n"
809         "undef"   -> outCLine pos++"#undef "  ++arg++"\n"
810         "def"     -> outCLine pos++case arg of
811             's':'t':'r':'u':'c':'t':' ':_ -> arg++"\n"
812             't':'y':'p':'e':'d':'e':'f':' ':_ -> arg++"\n"
813             'i':'n':'l':'i':'n':'e':' ':_ ->
814                 "#ifdef __GNUC__\n" ++
815                 "extern\n" ++
816                 "#endif\n"++
817                 arg++"\n"
818             _ -> "extern "++header++";\n"
819           where header = takeWhile (\c -> c /= '{' && c /= '=') arg
820         _ | conditional key -> outCLine pos++"#"++key++" "++arg++"\n"
821         _ -> ""
822
823 outTokenC :: (SourcePos, String, String) -> String
824 outTokenC (pos, key, arg) =
825     case key of
826         "def" -> case arg of
827             's':'t':'r':'u':'c':'t':' ':_ -> ""
828             't':'y':'p':'e':'d':'e':'f':' ':_ -> ""
829             'i':'n':'l':'i':'n':'e':' ':arg' ->
830                 case span (\c -> c /= '{' && c /= '=') arg' of
831                 (header, body) ->
832                     outCLine pos++
833                     "#ifndef __GNUC__\n" ++
834                     "extern inline\n" ++
835                     "#endif\n"++
836                     header++
837                     "\n#ifndef __GNUC__\n" ++
838                     ";\n" ++
839                     "#else\n"++
840                     body++
841                     "\n#endif\n"
842             _ -> outCLine pos++arg++"\n"
843         _ | conditional key -> outCLine pos++"#"++key++" "++arg++"\n"
844         _ -> ""
845
846 conditional :: String -> Bool
847 conditional "if"      = True
848 conditional "ifdef"   = True
849 conditional "ifndef"  = True
850 conditional "elif"    = True
851 conditional "else"    = True
852 conditional "endif"   = True
853 conditional "error"   = True
854 conditional "warning" = True
855 conditional _         = False
856
857 outCLine :: SourcePos -> String
858 outCLine (SourcePos name line) =
859     "#line "++show line++" \""++showCString (snd (splitName name))++"\"\n"
860
861 outHsLine :: SourcePos -> String
862 outHsLine (SourcePos name line) =
863     "    hsc_line ("++show (line + 1)++", \""++
864     showCString name++"\");\n"
865
866 showCString :: String -> String
867 showCString = concatMap showCChar
868     where
869     showCChar '\"' = "\\\""
870     showCChar '\'' = "\\\'"
871     showCChar '?'  = "\\?"
872     showCChar '\\' = "\\\\"
873     showCChar c | c >= ' ' && c <= '~' = [c]
874     showCChar '\a' = "\\a"
875     showCChar '\b' = "\\b"
876     showCChar '\f' = "\\f"
877     showCChar '\n' = "\\n\"\n           \""
878     showCChar '\r' = "\\r"
879     showCChar '\t' = "\\t"
880     showCChar '\v' = "\\v"
881     showCChar c    = ['\\',
882                       intToDigit (ord c `quot` 64),
883                       intToDigit (ord c `quot` 8 `mod` 8),
884                       intToDigit (ord c          `mod` 8)]
885
886 -----------------------------------------
887 -- Modified version from ghc/compiler/SysTools
888 -- Convert paths foo/baz to foo\baz on Windows
889
890 subst :: Char -> Char -> String -> String
891 #if defined(mingw32_HOST_OS) || defined(__CYGWIN32__)
892 subst a b = map (\x -> if x == a then b else x)
893 #else
894 subst _ _ = id
895 #endif
896
897 dosifyPath :: String -> String
898 dosifyPath = subst '/' '\\'
899
900 -- (getExecDir cmd) returns the directory in which the current
901 --                  executable, which should be called 'cmd', is running
902 -- So if the full path is /a/b/c/d/e, and you pass "d/e" as cmd,
903 -- you'll get "/a/b/c" back as the result
904 getExecDir :: String -> IO (Maybe String)
905 getExecDir cmd =
906     getExecPath >>= maybe (return Nothing) removeCmdSuffix
907     where unDosifyPath = subst '\\' '/'
908           initN n = reverse . drop n . reverse
909           removeCmdSuffix = return . Just . initN (length cmd) . unDosifyPath
910
911 getExecPath :: IO (Maybe String)
912 #if defined(__HUGS__)
913 getExecPath = liftM Just getProgName
914 #elif defined(mingw32_HOST_OS)
915 getExecPath =
916      allocaArray len $ \buf -> do
917          ret <- getModuleFileName nullPtr buf len
918          if ret == 0 then return Nothing
919                      else liftM Just $ peekCString buf
920     where len = 2048 -- Plenty, PATH_MAX is 512 under Win32.
921
922 foreign import stdcall unsafe "GetModuleFileNameA"
923     getModuleFileName :: Ptr () -> CString -> Int -> IO Int32
924 #else
925 getExecPath = return Nothing
926 #endif