[project @ 2003-12-10 14:15:16 by simonmar]
[ghc-hetmet.git] / ghc / compiler / ghci / InteractiveUI.hs
index d1b6b77..a1ec764 100644 (file)
@@ -1,68 +1,75 @@
-{-# OPTIONS -#include "Linker.h" -#include "SchedAPI.h" #-}
+{-# OPTIONS -#include "Linker.h" #-}
 -----------------------------------------------------------------------------
 -----------------------------------------------------------------------------
--- $Id: InteractiveUI.hs,v 1.105 2002/01/03 17:09:15 simonmar Exp $
+-- $Id: InteractiveUI.hs,v 1.162 2003/12/10 14:15:21 simonmar Exp $
 --
 -- GHC Interactive User Interface
 --
 -- (c) The GHC Team 2000
 --
 -----------------------------------------------------------------------------
 --
 -- GHC Interactive User Interface
 --
 -- (c) The GHC Team 2000
 --
 -----------------------------------------------------------------------------
-module InteractiveUI ( interactiveUI, ghciWelcomeMsg ) where
+module InteractiveUI ( 
+       interactiveUI,  -- :: CmState -> [FilePath] -> IO ()
+       ghciWelcomeMsg
+   ) where
 
 #include "../includes/config.h"
 #include "HsVersions.h"
 
 
 #include "../includes/config.h"
 #include "HsVersions.h"
 
-import Packages
 import CompManager
 import CompManager
-import HscTypes                ( TyThing(..) )
-import MkIface
-import ByteCodeLink
+import HscTypes                ( TyThing(..), HomeModInfo(hm_linkable), HomePackageTable,
+                         isObjectLinkable, GhciMode(..) )
+import IfaceSyn                ( IfaceDecl( ifName ) )
 import DriverFlags
 import DriverState
 import DriverFlags
 import DriverState
-import DriverUtil
-import Linker
-import Finder          ( flushPackageCache )
+import DriverUtil      ( remove_spaces )
+import Linker          ( showLinkerState, linkPackages )
 import Util
 import Util
-import Id              ( isRecordSelector, recordSelectorFieldLabel, 
-                         isDataConWrapId, idName )
-import Class           ( className )
-import TyCon           ( tyConName, tyConClass_maybe, isPrimTyCon )
-import FieldLabel      ( fieldLabelTyCon )
-import SrcLoc          ( isGoodSrcLoc )
+import Module          ( showModMsg, lookupModuleEnv )
 import Name            ( Name, isHomePackageName, nameSrcLoc, nameOccName,
                          NamedThing(..) )
 import OccName         ( isSymOcc )
 import Name            ( Name, isHomePackageName, nameSrcLoc, nameOccName,
                          NamedThing(..) )
 import OccName         ( isSymOcc )
-import BasicTypes      ( defaultFixity )
+import BasicTypes      ( defaultFixity, SuccessFlag(..) )
+import Packages
 import Outputable
 import Outputable
-import CmdLineOpts     ( DynFlag(..), getDynFlags, saveDynFlags, restoreDynFlags, dopt_unset )
-import Panic           ( GhcException(..), showGhcException )
+import CmdLineOpts     ( DynFlag(..), DynFlags(..), getDynFlags, saveDynFlags,
+                         restoreDynFlags, dopt_unset )
+import Panic           hiding ( showException )
 import Config
 
 import Config
 
-#ifndef mingw32_TARGET_OS
-import Posix
+#ifndef mingw32_HOST_OS
+import DriverUtil( handle )
+import System.Posix
+#if __GLASGOW_HASKELL__ > 504
+       hiding (getEnv)
+#endif
 #endif
 
 #endif
 
-import Exception
-import Dynamic
 #if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
 #if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
-import Readline 
+import Control.Concurrent      ( yield )       -- Used in readline loop
+import System.Console.Readline as Readline
 #endif
 #endif
-import Concurrent
-import IOExts
-import SystemExts
+
+--import SystemExts
+
+import Control.Exception as Exception
+import Data.Dynamic
+import Control.Concurrent
 
 import Numeric
 
 import Numeric
-import List
-import System
-import CPUTime
-import Directory
-import IO
-import Char
-import Monad           ( when, join )
-
-import PrelGHC                 ( unsafeCoerce# )
-import Foreign         ( nullPtr )
-import CString         ( peekCString )
+import Data.List
+import System.Cmd
+import System.CPUTime
+import System.Environment
+import System.Directory
+import System.IO as IO
+import Data.Char
+import Control.Monad as Monad
+
+import GHC.Exts                ( unsafeCoerce# )
+
+import Data.IORef      ( IORef, newIORef, readIORef, writeIORef )
+
+import System.Posix.Internals ( setNonBlockingFD )
 
 -----------------------------------------------------------------------------
 
 
 -----------------------------------------------------------------------------
 
@@ -77,16 +84,18 @@ GLOBAL_VAR(commands, builtin_commands, [(String, String -> GHCi Bool)])
 
 builtin_commands :: [(String, String -> GHCi Bool)]
 builtin_commands = [
 
 builtin_commands :: [(String, String -> GHCi Bool)]
 builtin_commands = [
-  ("add",      keepGoing addModule),
+  ("add",      keepGoingPaths addModule),
+  ("browse",    keepGoing browseCmd),
   ("cd",       keepGoing changeDirectory),
   ("def",      keepGoing defineMacro),
   ("help",     keepGoing help),
   ("?",                keepGoing help),
   ("info",      keepGoing info),
   ("cd",       keepGoing changeDirectory),
   ("def",      keepGoing defineMacro),
   ("help",     keepGoing help),
   ("?",                keepGoing help),
   ("info",      keepGoing info),
-  ("load",     keepGoing loadModule),
+  ("load",     keepGoingPaths loadModule),
   ("module",   keepGoing setContext),
   ("reload",   keepGoing reloadModule),
   ("set",      keepGoing setCmd),
   ("module",   keepGoing setContext),
   ("reload",   keepGoing reloadModule),
   ("set",      keepGoing setCmd),
+  ("show",     keepGoing showCmd),
   ("type",     keepGoing typeOfExpr),
   ("unset",    keepGoing unsetOptions),
   ("undef",     keepGoing undefineMacro),
   ("type",     keepGoing typeOfExpr),
   ("unset",    keepGoing unsetOptions),
   ("undef",     keepGoing undefineMacro),
@@ -96,31 +105,41 @@ builtin_commands = [
 keepGoing :: (String -> GHCi ()) -> (String -> GHCi Bool)
 keepGoing a str = a str >> return False
 
 keepGoing :: (String -> GHCi ()) -> (String -> GHCi Bool)
 keepGoing a str = a str >> return False
 
+keepGoingPaths :: ([FilePath] -> GHCi ()) -> (String -> GHCi Bool)
+keepGoingPaths a str = a (toArgs str) >> return False
+
 shortHelpText = "use :? for help.\n"
 
 shortHelpText = "use :? for help.\n"
 
+-- NOTE: spaces at the end of each line to workaround CPP/string gap bug.
 helpText = "\ 
 \ Commands available from the prompt:\n\ 
 helpText = "\ 
 \ Commands available from the prompt:\n\ 
-\\
-\   <stmt>                evaluate/run <stmt>\n\ 
-\   :add <filename> ...    add module(s) to the current target set\n\ 
-\   :cd <dir>             change directory to <dir>\n\ 
-\   :def <cmd> <expr>      define a command :<cmd>\n\ 
-\   :help, :?             display this list of commands\n\ 
-\   :info [<name> ...]     display information about the given names\n\ 
-\   :load <filename> ...   load module(s) and their dependents\n\ 
-\   :module <mod>         set the context for expression evaluation to <mod>\n\ 
-\   :reload               reload the current module set\n\ 
-\   :set <option> ...     set options\n\ 
-\   :set args <arg> ...           set the arguments returned by System.getArgs\n\ 
-\   :set prog <progname>   set the value returned by System.getProgName\n\ 
-\   :undef <cmd>          undefine user-defined command :<cmd>\n\ 
-\   :type <expr>          show the type of <expr>\n\ 
-\   :unset <option> ...           unset options\n\ 
-\   :quit                 exit GHCi\n\ 
-\   :!<command>                   run the shell command <command>\n\ 
-\\ 
+\\n\ 
+\   <stmt>                    evaluate/run <stmt>\n\ 
+\   :add <filename> ...        add module(s) to the current target set\n\ 
+\   :browse [*]<module>               display the names defined by <module>\n\ 
+\   :cd <dir>                 change directory to <dir>\n\ 
+\   :def <cmd> <expr>          define a command :<cmd>\n\ 
+\   :help, :?                 display this list of commands\n\ 
+\   :info [<name> ...]         display information about the given names\n\ 
+\   :load <filename> ...       load module(s) and their dependents\n\ 
+\   :module [+/-] [*]<mod> ... set the context for expression evaluation\n\ 
+\   :reload                   reload the current module set\n\ 
+\\n\ 
+\   :set <option> ...         set options\n\ 
+\   :set args <arg> ...               set the arguments returned by System.getArgs\n\ 
+\   :set prog <progname>       set the value returned by System.getProgName\n\ 
+\\n\ 
+\   :show modules             show the currently loaded modules\n\ 
+\   :show bindings            show the current bindings made at the prompt\n\ 
+\\n\ 
+\   :type <expr>              show the type of <expr>\n\ 
+\   :undef <cmd>              undefine user-defined command :<cmd>\n\ 
+\   :unset <option> ...               unset options\n\ 
+\   :quit                     exit GHCi\n\ 
+\   :!<command>                       run the shell command <command>\n\ 
+\\n\ 
 \ Options for `:set' and `:unset':\n\ 
 \ Options for `:set' and `:unset':\n\ 
-\\ 
+\\n\ 
 \    +r                        revert top-level expressions after each evaluation\n\ 
 \    +s                 print timing/memory stats after each evaluation\n\ 
 \    +t                        print type after evaluation\n\ 
 \    +r                        revert top-level expressions after each evaluation\n\ 
 \    +s                 print timing/memory stats after each evaluation\n\ 
 \    +t                        print type after evaluation\n\ 
@@ -128,44 +147,33 @@ helpText = "\
 \                         (eg. -v2, -fglasgow-exts, etc.)\n\ 
 \"
 
 \                         (eg. -v2, -fglasgow-exts, etc.)\n\ 
 \"
 
-interactiveUI :: CmState -> [FilePath] -> [LibrarySpec] -> IO ()
-interactiveUI cmstate paths cmdline_libs = do
-   hFlush stdout
-   hSetBuffering stdout NoBuffering
+interactiveUI :: [FilePath] -> Maybe String -> IO ()
+interactiveUI srcs maybe_expr = do
+   dflags <- getDynFlags
 
 
-   -- link in the available packages
-   pkgs <- getPackageInfo
-   initLinker
-   linkPackages cmdline_libs pkgs
+   cmstate <- cmInit Interactive dflags;
 
 
-   dflags <- getDynFlags
+   hFlush stdout
+   hSetBuffering stdout NoBuffering
 
 
-   (cmstate, maybe_hval) 
-       <- cmCompileExpr cmstate dflags "IO.hSetBuffering IO.stdout IO.NoBuffering Prelude.>> IO.hSetBuffering IO.stderr IO.NoBuffering"
-   case maybe_hval of
-       Just hval -> unsafeCoerce# hval :: IO ()
-       _ -> panic "interactiveUI:buffering"
+       -- Initialise buffering for the *interpreted* I/O system
+   initInterpBuffering cmstate
 
 
-   (cmstate, maybe_hval)
-       <- cmCompileExpr cmstate dflags "IO.hFlush PrelHandle.stderr"
-   case maybe_hval of
-       Just hval -> writeIORef flush_stderr (unsafeCoerce# hval :: IO ())
-       _ -> panic "interactiveUI:stderr"
+       -- We don't want the cmd line to buffer any input that might be
+       -- intended for the program, so unbuffer stdin.
+   hSetBuffering stdin NoBuffering
 
 
-   (cmstate, maybe_hval) 
-       <- cmCompileExpr cmstate dflags "IO.hFlush PrelHandle.stdout"
-   case maybe_hval of
-       Just hval -> writeIORef flush_stdout (unsafeCoerce# hval :: IO ())
-       _ -> panic "interactiveUI:stdout"
+       -- initial context is just the Prelude
+   cmstate <- cmSetContext cmstate [] ["Prelude"]
 
 #if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
    Readline.initialize
 #endif
 
 
 #if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
    Readline.initialize
 #endif
 
-   startGHCi (runGHCi paths) 
+   startGHCi (runGHCi srcs dflags maybe_expr)
        GHCiState{ progname = "<interactive>",
                   args = [],
        GHCiState{ progname = "<interactive>",
                   args = [],
-                  targets = paths,
+                  targets = srcs,
                   cmstate = cmstate,
                   options = [] }
 
                   cmstate = cmstate,
                   options = [] }
 
@@ -175,9 +183,8 @@ interactiveUI cmstate paths cmdline_libs = do
 
    return ()
 
 
    return ()
 
-
-runGHCi :: [FilePath] -> GHCi ()
-runGHCi paths = do
+runGHCi :: [FilePath] -> DynFlags -> Maybe String -> GHCi ()
+runGHCi paths dflags maybe_expr = do
   read_dot_files <- io (readIORef v_Read_DotGHCi)
 
   when (read_dot_files) $ do
   read_dot_files <- io (readIORef v_Read_DotGHCi)
 
   when (read_dot_files) $ do
@@ -209,28 +216,42 @@ runGHCi paths = do
                  Left e    -> return ()
                  Right hdl -> fileLoop hdl False
 
                  Left e    -> return ()
                  Right hdl -> fileLoop hdl False
 
-  -- perform a :load for files given on the GHCi command line
+  -- Perform a :load for files given on the GHCi command line
   when (not (null paths)) $
      ghciHandle showException $
   when (not (null paths)) $
      ghciHandle showException $
-       loadModule (unwords paths)
-
-  -- enter the interactive loop
-  interactiveLoop
+       loadModule paths
+
+  -- if verbosity is greater than 0, or we are connected to a
+  -- terminal, display the prompt in the interactive loop.
+  is_tty <- io (hIsTerminalDevice stdin)
+  let show_prompt = verbosity dflags > 0 || is_tty
+
+  case maybe_expr of
+       Nothing -> 
+           -- enter the interactive loop
+           interactiveLoop is_tty show_prompt
+       Just expr -> do
+           -- just evaluate the expression we were given
+           runCommand expr
+           return ()
 
   -- and finally, exit
 
   -- and finally, exit
-  io $ do putStrLn "Leaving GHCi."
+  io $ do when (verbosity dflags > 0) $ putStrLn "Leaving GHCi."
 
 
 
 
-interactiveLoop = do
-  -- ignore ^C exceptions caught here
-  ghciHandleDyn (\e -> case e of Interrupted -> ghciUnblock interactiveLoop
-                                _other      -> return ()) $ do
+interactiveLoop is_tty show_prompt = do
+  -- Ignore ^C exceptions caught here
+  ghciHandleDyn (\e -> case e of 
+                       Interrupted -> ghciUnblock (interactiveLoop is_tty show_prompt)
+                       _other      -> return ()) $ do
 
   -- read commands from stdin
 #if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
 
   -- read commands from stdin
 #if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
-  readlineLoop
+  if (is_tty) 
+       then readlineLoop
+       else fileLoop stdin show_prompt
 #else
 #else
-  fileLoop stdin True
+  fileLoop stdin show_prompt
 #endif
 
 
 #endif
 
 
@@ -245,10 +266,10 @@ interactiveLoop = do
 
 checkPerms :: String -> IO Bool
 checkPerms name =
 
 checkPerms :: String -> IO Bool
 checkPerms name =
-  handle (\_ -> return False) $ do
-#ifdef mingw32_TARGET_OS
-     doesFileExist name
+#ifdef mingw32_HOST_OS
+  return True
 #else
 #else
+  DriverUtil.handle (\_ -> return False) $ do
      st <- getFileStatus name
      me <- getRealUserID
      if fileOwner st /= me then do
      st <- getFileStatus name
      me <- getRealUserID
      if fileOwner st /= me then do
@@ -267,13 +288,13 @@ checkPerms name =
 
 fileLoop :: Handle -> Bool -> GHCi ()
 fileLoop hdl prompt = do
 
 fileLoop :: Handle -> Bool -> GHCi ()
 fileLoop hdl prompt = do
-   st <- getGHCiState
-   mod <- io (cmGetContext (cmstate st))
-   when prompt (io (putStr (mod ++ "> ")))
+   cmstate <- getCmState
+   (mod,imports) <- io (cmGetContext cmstate)
+   when prompt (io (putStr (mkPrompt mod imports)))
    l <- io (IO.try (hGetLine hdl))
    case l of
        Left e | isEOFError e -> return ()
    l <- io (IO.try (hGetLine hdl))
    case l of
        Left e | isEOFError e -> return ()
-              | otherwise    -> throw e
+              | otherwise    -> io (ioError e)
        Right l -> 
          case remove_spaces l of
            "" -> fileLoop hdl prompt
        Right l -> 
          case remove_spaces l of
            "" -> fileLoop hdl prompt
@@ -283,19 +304,24 @@ fileLoop hdl prompt = do
 stringLoop :: [String] -> GHCi ()
 stringLoop [] = return ()
 stringLoop (s:ss) = do
 stringLoop :: [String] -> GHCi ()
 stringLoop [] = return ()
 stringLoop (s:ss) = do
-   st <- getGHCiState
    case remove_spaces s of
        "" -> stringLoop ss
        l  -> do quit <- runCommand l
                  if quit then return () else stringLoop ss
 
    case remove_spaces s of
        "" -> stringLoop ss
        l  -> do quit <- runCommand l
                  if quit then return () else stringLoop ss
 
+mkPrompt toplevs exports
+   = concat (intersperse " " (map ('*':) toplevs ++ exports)) ++ "> "
+
 #if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
 readlineLoop :: GHCi ()
 readlineLoop = do
 #if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
 readlineLoop :: GHCi ()
 readlineLoop = do
-   st <- getGHCiState
-   mod <- io (cmGetContext (cmstate st))
+   cmstate <- getCmState
+   (mod,imports) <- io (cmGetContext cmstate)
    io yield
    io yield
-   l <- io (readline (mod ++ "> "))
+   l <- io (readline (mkPrompt mod imports)
+               `finally` setNonBlockingFD 0)
+               -- readline sometimes puts stdin into blocking mode,
+               -- so we need to put it back for the IO library
    case l of
        Nothing -> return ()
        Just l  ->
    case l of
        Nothing -> return ()
        Just l  ->
@@ -307,16 +333,23 @@ readlineLoop = do
                  if quit then return () else readlineLoop
 #endif
 
                  if quit then return () else readlineLoop
 #endif
 
--- Top level exception handler, just prints out the exception 
--- and carries on.
 runCommand :: String -> GHCi Bool
 runCommand :: String -> GHCi Bool
-runCommand c = 
-  ghciHandle ( \exception -> do
-               flushEverything
-               showException exception
-               return False
-            ) $
-  doCommand c
+runCommand c = ghciHandle handler (doCommand c)
+
+-- This is the exception handler for exceptions generated by the
+-- user's code; it normally just prints out the exception.  The
+-- handler must be recursive, in case showing the exception causes
+-- more exceptions to be raised.
+--
+-- Bugfix: if the user closed stdout or stderr, the flushing will fail,
+-- raising another exception.  We therefore don't put the recursive
+-- handler arond the flushing operation, so if stderr is closed
+-- GHCi will just die gracefully rather than going into an infinite loop.
+handler :: Exception -> GHCi Bool
+handler exception = do
+  flushInterpBuffers
+  io installSignalHandlers
+  ghciHandle handler (showException exception >> return False)
 
 showException (DynException dyn) =
   case fromDynamic dyn of
 
 showException (DynException dyn) =
   case fromDynamic dyn of
@@ -340,26 +373,27 @@ runStmt stmt
  | otherwise
  = do st <- getGHCiState
       dflags <- io getDynFlags
  | otherwise
  = do st <- getGHCiState
       dflags <- io getDynFlags
-      let dflags' = dopt_unset dflags Opt_WarnUnusedBinds
+      let cm_state' = cmSetDFlags (cmstate st)
+                                 (dopt_unset dflags Opt_WarnUnusedBinds)
       (new_cmstate, result) <- 
        io $ withProgName (progname st) $ withArgs (args st) $
       (new_cmstate, result) <- 
        io $ withProgName (progname st) $ withArgs (args st) $
-       cmRunStmt (cmstate st) dflags' stmt
+            cmRunStmt cm_state' stmt
       setGHCiState st{cmstate = new_cmstate}
       case result of
        CmRunFailed      -> return []
        CmRunException e -> showException e >> return []
       setGHCiState st{cmstate = new_cmstate}
       case result of
        CmRunFailed      -> return []
        CmRunException e -> showException e >> return []
-       CmRunDeadlocked  -> io (putStrLn "Deadlocked.") >> return []
        CmRunOk names    -> return names
 
 -- possibly print the type and revert CAFs after evaluating an expression
 finishEvalExpr names
  = do b <- isOptionSet ShowType
        CmRunOk names    -> return names
 
 -- possibly print the type and revert CAFs after evaluating an expression
 finishEvalExpr names
  = do b <- isOptionSet ShowType
-      st <- getGHCiState
-      when b (mapM_ (showTypeOfName (cmstate st)) names)
+      cmstate <- getCmState
+      when b (mapM_ (showTypeOfName cmstate) names)
 
 
+      flushInterpBuffers
+      io installSignalHandlers
       b <- isOptionSet RevertCAFs
       io (when b revertCAFs)
       b <- isOptionSet RevertCAFs
       io (when b revertCAFs)
-      flushEverything
       return True
 
 showTypeOfName :: CmState -> Name -> GHCi ()
       return True
 
 showTypeOfName :: CmState -> Name -> GHCi ()
@@ -369,12 +403,6 @@ showTypeOfName cmstate n
          Nothing  -> return ()
          Just str -> io (putStrLn (showSDoc (ppr n) ++ " :: " ++ str))
 
          Nothing  -> return ()
          Just str -> io (putStrLn (showSDoc (ppr n) ++ " :: " ++ str))
 
-flushEverything :: GHCi ()
-flushEverything
-   = io $ do Monad.join (readIORef flush_stdout)
-            Monad.join (readIORef flush_stderr)
-             return ()
-
 specialCommand :: String -> GHCi Bool
 specialCommand ('!':str) = shellEscape (dropWhile isSpace str)
 specialCommand str = do
 specialCommand :: String -> GHCi Bool
 specialCommand ('!':str) = shellEscape (dropWhile isSpace str)
 specialCommand str = do
@@ -391,6 +419,46 @@ specialCommand str = do
 
 noArgs c = throwDyn (CmdLineError ("command `" ++ c ++ "' takes no arguments"))
 
 
 noArgs c = throwDyn (CmdLineError ("command `" ++ c ++ "' takes no arguments"))
 
+
+-----------------------------------------------------------------------------
+-- To flush buffers for the *interpreted* computation we need
+-- to refer to *its* stdout/stderr handles
+
+GLOBAL_VAR(flush_interp,       error "no flush_interp", IO ())
+GLOBAL_VAR(turn_off_buffering, error "no flush_stdout", IO ())
+
+no_buf_cmd = "IO.hSetBuffering IO.stdout IO.NoBuffering" ++
+            " Prelude.>> IO.hSetBuffering IO.stderr IO.NoBuffering"
+flush_cmd  = "IO.hFlush IO.stdout Prelude.>> IO.hFlush IO.stderr"
+
+initInterpBuffering :: CmState -> IO ()
+initInterpBuffering cmstate
+ = do maybe_hval <- cmCompileExpr cmstate no_buf_cmd
+       
+      case maybe_hval of
+       Just hval -> writeIORef turn_off_buffering (unsafeCoerce# hval :: IO ())
+       other     -> panic "interactiveUI:setBuffering"
+       
+      maybe_hval <- cmCompileExpr cmstate flush_cmd
+      case maybe_hval of
+       Just hval -> writeIORef flush_interp (unsafeCoerce# hval :: IO ())
+       _         -> panic "interactiveUI:flush"
+
+      turnOffBuffering -- Turn it off right now
+
+      return ()
+
+
+flushInterpBuffers :: GHCi ()
+flushInterpBuffers
+ = io $ do Monad.join (readIORef flush_interp)
+           return ()
+
+turnOffBuffering :: IO ()
+turnOffBuffering
+ = do Monad.join (readIORef turn_off_buffering)
+      return ()
+
 -----------------------------------------------------------------------------
 -- Commands
 
 -----------------------------------------------------------------------------
 -- Commands
 
@@ -401,31 +469,37 @@ info :: String -> GHCi ()
 info "" = throwDyn (CmdLineError "syntax: `:i <thing-you-want-info-about>'")
 info s = do
   let names = words s
 info "" = throwDyn (CmdLineError "syntax: `:i <thing-you-want-info-about>'")
 info s = do
   let names = words s
-  state <- getGHCiState
-  dflags <- io getDynFlags
+  init_cms <- getCmState
   let 
     infoThings cms [] = return cms
     infoThings cms (name:names) = do
   let 
     infoThings cms [] = return cms
     infoThings cms (name:names) = do
-      (cms, unqual, stuff) <- io (cmInfoThing cms dflags name)
+      stuff <- io (cmInfoThing cms name)
       io (putStrLn (showSDocForUser unqual (
            vcat (intersperse (text "") (map showThing stuff))))
          )
       infoThings cms names
 
       io (putStrLn (showSDocForUser unqual (
            vcat (intersperse (text "") (map showThing stuff))))
          )
       infoThings cms names
 
-    showThing (ty_thing, fixity) 
-       = vcat [ text "-- " <> showTyThing ty_thing, 
-                showFixity fixity (getName ty_thing),
-                ppr (ifaceTyThing ty_thing) ]
+    unqual = cmGetPrintUnqual init_cms
+
+    showThing (decl, fixity) 
+       = vcat [ text "-- " <> showTyThing decl, 
+                showFixity fixity (ifName decl),
+                showTyThing decl ]
 
     showFixity fix name
        | fix == defaultFixity = empty
        | otherwise            = ppr fix <+> 
 
     showFixity fix name
        | fix == defaultFixity = empty
        | otherwise            = ppr fix <+> 
-                                (if isSymOcc (nameOccName name)
-                                       then ppr name
-                                       else char '`' <> ppr name <> char '`')
+                                (if isSymOcc name
+                                 then ppr name
+                                 else char '`' <> ppr name <> char '`')
 
 
+    showTyThing decl = ppr decl
+
+{-
     showTyThing (AClass cl)
        = hcat [ppr cl, text " is a class", showSrcLoc (className cl)]
     showTyThing (AClass cl)
        = hcat [ppr cl, text " is a class", showSrcLoc (className cl)]
+    showTyThing (ADataCon dc)
+       = hcat [ppr dc, text " is a data constructor", showSrcLoc (dataConName dc)]
     showTyThing (ATyCon ty)
        | isPrimTyCon ty
        = hcat [ppr ty, text " is a primitive type constructor"]
     showTyThing (ATyCon ty)
        | isPrimTyCon ty
        = hcat [ppr ty, text " is a primitive type constructor"]
@@ -435,13 +509,10 @@ info s = do
        = hcat [ppr id, text " is a ", idDescr id, showSrcLoc (idName id)]
 
     idDescr id
        = hcat [ppr id, text " is a ", idDescr id, showSrcLoc (idName id)]
 
     idDescr id
-       | isRecordSelector id = 
-               case tyConClass_maybe (fieldLabelTyCon (
-                               recordSelectorFieldLabel id)) of
-                       Nothing -> text "record selector"
-                       Just c  -> text "method in class " <> ppr c
-       | isDataConWrapId id  = text "data constructor"
-       | otherwise           = text "variable"
+       = case globalIdDetails id of
+           RecordSelId lbl -> text "record selector for type" <+> ppr (fieldLabelTyCon lbl)
+           ClassOpId cls   -> text "method in class" <+> ppr cls
+                   otherwise       -> text "variable"
 
        -- also print out the source location for home things
     showSrcLoc name
 
        -- also print out the source location for home things
     showSrcLoc name
@@ -450,41 +521,35 @@ info s = do
        | otherwise
        = empty
        where loc = nameSrcLoc name
        | otherwise
        = empty
        where loc = nameSrcLoc name
+-}
 
 
-  cms <- infoThings (cmstate state) names
-  setGHCiState state{ cmstate = cms }
+  infoThings init_cms names
   return ()
 
   return ()
 
-
-addModule :: String -> GHCi ()
-addModule str = do
-  let files = words str
+addModule :: [FilePath] -> GHCi ()
+addModule files = do
   state <- getGHCiState
   state <- getGHCiState
-  dflags <- io (getDynFlags)
   io (revertCAFs)                      -- always revert CAFs on load/add.
   io (revertCAFs)                      -- always revert CAFs on load/add.
+  files <- mapM expandPath files
   let new_targets = files ++ targets state 
   let new_targets = files ++ targets state 
-  graph <- io (cmDepAnal (cmstate state) dflags new_targets)
-  (cmstate1, ok, mods) <- io (cmLoadModules (cmstate state) dflags graph)
+  graph <- io (cmDepAnal (cmstate state) new_targets)
+  (cmstate1, ok, mods) <- io (cmLoadModules (cmstate state) graph)
   setGHCiState state{ cmstate = cmstate1, targets = new_targets }
   setGHCiState state{ cmstate = cmstate1, targets = new_targets }
-  modulesLoadedMsg ok mods
-
-setContext :: String -> GHCi ()
-setContext ""
-  = throwDyn (CmdLineError "syntax: `:m <module>'")
-setContext m | not (isUpper (head m)) || not (all isAlphaNumEx (tail m))
-  = throwDyn (CmdLineError ("strange looking module name: `" ++ m ++ "'"))
-    where
-       isAlphaNumEx c = isAlphaNum c || c == '_'
-setContext str
-  = do st <- getGHCiState
-       new_cmstate <- io (cmSetContext (cmstate st) str)
-       setGHCiState st{cmstate=new_cmstate}
+  setContextAfterLoad mods
+  dflags <- io getDynFlags
+  modulesLoadedMsg ok mods dflags
 
 changeDirectory :: String -> GHCi ()
 
 changeDirectory :: String -> GHCi ()
-changeDirectory ('~':d) = do
-   tilde <- io (getEnv "HOME") -- will fail if HOME not defined
-   io (setCurrentDirectory (tilde ++ '/':d))
-changeDirectory d = io (setCurrentDirectory d)
+changeDirectory dir = do
+  state    <- getGHCiState
+  when (targets state /= []) $
+       io $ putStr "Warning: changing directory causes all loaded modules to be unloaded, \n\ 
+       \because the search path has changed.\n"
+  cmstate1 <- io (cmUnload (cmstate state))
+  setGHCiState state{ cmstate = cmstate1, targets = [] }
+  setContextAfterLoad []
+  dir <- expandPath dir
+  io (setCurrentDirectory dir)
 
 defineMacro :: String -> GHCi ()
 defineMacro s = do
 
 defineMacro :: String -> GHCi ()
 defineMacro s = do
@@ -503,10 +568,8 @@ defineMacro s = do
   let new_expr = '(' : definition ++ ") :: String -> IO String"
 
   -- compile the expression
   let new_expr = '(' : definition ++ ") :: String -> IO String"
 
   -- compile the expression
-  st <- getGHCiState
-  dflags <- io getDynFlags
-  (new_cmstate, maybe_hv) <- io (cmCompileExpr (cmstate st) dflags new_expr)
-  setGHCiState st{cmstate = new_cmstate}
+  cms <- getCmState
+  maybe_hv <- io (cmCompileExpr cms new_expr)
   case maybe_hv of
      Nothing -> return ()
      Just hv -> io (writeIORef commands --
   case maybe_hv of
      Nothing -> return ()
      Just hv -> io (writeIORef commands --
@@ -530,68 +593,77 @@ undefineMacro macro_name = do
        else do
   io (writeIORef commands (filter ((/= macro_name) . fst) cmds))
 
        else do
   io (writeIORef commands (filter ((/= macro_name) . fst) cmds))
 
-loadModule :: String -> GHCi ()
-loadModule str = timeIt (loadModule' str)
 
 
-loadModule' str = do
-  let files = words str
+loadModule :: [FilePath] -> GHCi ()
+loadModule fs = timeIt (loadModule' fs)
+
+loadModule' :: [FilePath] -> GHCi ()
+loadModule' files = do
   state <- getGHCiState
   state <- getGHCiState
-  dflags <- io getDynFlags
+
+  -- expand tildes
+  files <- mapM expandPath files
 
   -- do the dependency anal first, so that if it fails we don't throw
   -- away the current set of modules.
 
   -- do the dependency anal first, so that if it fails we don't throw
   -- away the current set of modules.
-  graph <- io (cmDepAnal (cmstate state) dflags files)
+  graph <- io (cmDepAnal (cmstate state) files)
 
   -- Dependency anal ok, now unload everything
 
   -- Dependency anal ok, now unload everything
-  cmstate1 <- io (cmUnload (cmstate state) dflags)
+  cmstate1 <- io (cmUnload (cmstate state))
   setGHCiState state{ cmstate = cmstate1, targets = [] }
 
   io (revertCAFs)  -- always revert CAFs on load.
   setGHCiState state{ cmstate = cmstate1, targets = [] }
 
   io (revertCAFs)  -- always revert CAFs on load.
-  (cmstate2, ok, mods) <- io (cmLoadModules cmstate1 dflags graph)
-
+  (cmstate2, ok, mods) <- io (cmLoadModules cmstate1 graph)
   setGHCiState state{ cmstate = cmstate2, targets = files }
   setGHCiState state{ cmstate = cmstate2, targets = files }
-  modulesLoadedMsg ok mods
+
+  setContextAfterLoad mods
+  dflags <- io (getDynFlags)
+  modulesLoadedMsg ok mods dflags
 
 
 reloadModule :: String -> GHCi ()
 reloadModule "" = do
   state <- getGHCiState
 
 
 reloadModule :: String -> GHCi ()
 reloadModule "" = do
   state <- getGHCiState
-  dflags <- io getDynFlags
   case targets state of
    [] -> io (putStr "no current target\n")
    paths -> do
        -- do the dependency anal first, so that if it fails we don't throw
        -- away the current set of modules.
   case targets state of
    [] -> io (putStr "no current target\n")
    paths -> do
        -- do the dependency anal first, so that if it fails we don't throw
        -- away the current set of modules.
-       graph <- io (cmDepAnal (cmstate state) dflags paths)
+       graph <- io (cmDepAnal (cmstate state) paths)
 
        io (revertCAFs)         -- always revert CAFs on reload.
 
        io (revertCAFs)         -- always revert CAFs on reload.
-       (new_cmstate, ok, mods) 
-               <- io (cmLoadModules (cmstate state) dflags graph)
-
-        setGHCiState state{ cmstate=new_cmstate }
-       modulesLoadedMsg ok mods
+       (cmstate1, ok, mods) 
+               <- io (cmLoadModules (cmstate state) graph)
+        setGHCiState state{ cmstate=cmstate1 }
+       setContextAfterLoad mods
+       dflags <- io getDynFlags
+       modulesLoadedMsg ok mods dflags
 
 reloadModule _ = noArgs ":reload"
 
 
 reloadModule _ = noArgs ":reload"
 
+setContextAfterLoad [] = setContext prel
+setContextAfterLoad (m:_) = do
+  cmstate <- getCmState
+  b <- io (cmModuleIsInterpreted cmstate m)
+  if b then setContext ('*':m) else setContext m
 
 
-modulesLoadedMsg ok mods = do
-  let mod_commas 
+modulesLoadedMsg ok mods dflags =
+  when (verbosity dflags > 0) $ do
+   let mod_commas 
        | null mods = text "none."
        | otherwise = hsep (
            punctuate comma (map text mods)) <> text "."
        | null mods = text "none."
        | otherwise = hsep (
            punctuate comma (map text mods)) <> text "."
-  case ok of
-    False -> 
+   case ok of
+    Failed ->
        io (putStrLn (showSDoc (text "Failed, modules loaded: " <> mod_commas)))
        io (putStrLn (showSDoc (text "Failed, modules loaded: " <> mod_commas)))
-    True  -> 
+    Succeeded  ->
        io (putStrLn (showSDoc (text "Ok, modules loaded: " <> mod_commas)))
 
 
 typeOfExpr :: String -> GHCi ()
 typeOfExpr str 
        io (putStrLn (showSDoc (text "Ok, modules loaded: " <> mod_commas)))
 
 
 typeOfExpr :: String -> GHCi ()
 typeOfExpr str 
-  = do st <- getGHCiState
-       dflags <- io getDynFlags
-       (new_cmstate, maybe_tystr) <- io (cmTypeOfExpr (cmstate st) dflags str)
-       setGHCiState st{cmstate = new_cmstate}
+  = do cms <- getCmState
+       maybe_tystr <- io (cmTypeOfExpr cms str)
        case maybe_tystr of
          Nothing    -> return ()
          Just tystr -> io (putStrLn tystr)
        case maybe_tystr of
          Nothing    -> return ()
          Just tystr -> io (putStrLn tystr)
@@ -602,6 +674,96 @@ quit _ = return True
 shellEscape :: String -> GHCi Bool
 shellEscape str = io (system str >> return False)
 
 shellEscape :: String -> GHCi Bool
 shellEscape str = io (system str >> return False)
 
+-----------------------------------------------------------------------------
+-- Browing a module's contents
+
+browseCmd :: String -> GHCi ()
+browseCmd m = 
+  case words m of
+    ['*':m] | looksLikeModuleName m -> browseModule m False
+    [m]     | looksLikeModuleName m -> browseModule m True
+    _ -> throwDyn (CmdLineError "syntax:  :browse <module>")
+
+browseModule m exports_only = do
+  cms <- getCmState
+
+  is_interpreted <- io (cmModuleIsInterpreted cms m)
+  when (not is_interpreted && not exports_only) $
+       throwDyn (CmdLineError ("module `" ++ m ++ "' is not interpreted"))
+
+  -- Temporarily set the context to the module we're interested in,
+  -- just so we can get an appropriate PrintUnqualified
+  (as,bs) <- io (cmGetContext cms)
+  cms1 <- io (if exports_only then cmSetContext cms [] [prel,m]
+                             else cmSetContext cms [m] [])
+  cms2 <- io (cmSetContext cms1 as bs)
+
+  things <- io (cmBrowseModule cms2 m exports_only)
+
+  let unqual = cmGetPrintUnqual cms1 -- NOTE: cms1 with the new context
+
+  io (putStrLn (showSDocForUser unqual (
+        vcat (map ppr things)
+      )))
+
+-----------------------------------------------------------------------------
+-- Setting the module context
+
+setContext str
+  | all sensible mods = fn mods
+  | otherwise = throwDyn (CmdLineError "syntax:  :module [+/-] [*]M1 ... [*]Mn")
+  where
+    (fn, mods) = case str of 
+                       '+':stuff -> (addToContext,      words stuff)
+                       '-':stuff -> (removeFromContext, words stuff)
+                       stuff     -> (newContext,        words stuff) 
+
+    sensible ('*':m) = looksLikeModuleName m
+    sensible m       = looksLikeModuleName m
+
+newContext mods = do
+  cms <- getCmState
+  (as,bs) <- separate cms mods [] []
+  let bs' = if null as && prel `notElem` bs then prel:bs else bs
+  cms' <- io (cmSetContext cms as bs')
+  setCmState cms'
+
+separate cmstate []           as bs = return (as,bs)
+separate cmstate (('*':m):ms) as bs = do
+   b <- io (cmModuleIsInterpreted cmstate m)
+   if b then separate cmstate ms (m:as) bs
+       else throwDyn (CmdLineError ("module `" ++ m ++ "' is not interpreted"))
+separate cmstate (m:ms)       as bs = separate cmstate ms as (m:bs)
+
+prel = "Prelude"
+
+
+addToContext mods = do
+  cms <- getCmState
+  (as,bs) <- io (cmGetContext cms)
+
+  (as',bs') <- separate cms mods [] []
+
+  let as_to_add = as' \\ (as ++ bs)
+      bs_to_add = bs' \\ (as ++ bs)
+
+  cms' <- io (cmSetContext cms
+                       (as ++ as_to_add) (bs ++ bs_to_add))
+  setCmState cms'
+
+
+removeFromContext mods = do
+  cms <- getCmState
+  (as,bs) <- io (cmGetContext cms)
+
+  (as_to_remove,bs_to_remove) <- separate cms mods [] []
+
+  let as' = as \\ (as_to_remove ++ bs_to_remove)
+      bs' = bs \\ (as_to_remove ++ bs_to_remove)
+
+  cms' <- io (cmSetContext cms as' bs')
+  setCmState cms'
+
 ----------------------------------------------------------------------------
 -- Code for `:set'
 
 ----------------------------------------------------------------------------
 -- Code for `:set'
 
@@ -641,21 +803,28 @@ setProg _ = do
 setOptions wds =
    do -- first, deal with the GHCi opts (+s, +t, etc.)
       let (plus_opts, minus_opts)  = partition isPlus wds
 setOptions wds =
    do -- first, deal with the GHCi opts (+s, +t, etc.)
       let (plus_opts, minus_opts)  = partition isPlus wds
-      mapM setOpt plus_opts
+      mapM_ setOpt plus_opts
 
       -- now, the GHC flags
 
       -- now, the GHC flags
-      pkgs_before <- io (readIORef v_Packages)
+      pkgs_before <- io (readIORef v_ExplicitPackages)
       leftovers   <- io (processArgs static_flags minus_opts [])
       leftovers   <- io (processArgs static_flags minus_opts [])
-      pkgs_after  <- io (readIORef v_Packages)
+      pkgs_after  <- io (readIORef v_ExplicitPackages)
 
       -- update things if the users wants more packages
 
       -- update things if the users wants more packages
-      when (pkgs_before /= pkgs_after) $
-        newPackages (pkgs_after \\ pkgs_before)
+      let new_packages = pkgs_after \\ pkgs_before
+      when (not (null new_packages)) $
+        newPackages new_packages
+
+      -- don't forget about the extra command-line flags from the 
+      -- extra_ghc_opts fields in the new packages
+      new_package_details <- io (getPackageDetails new_packages)
+      let pkg_extra_opts = concatMap extra_ghc_opts new_package_details
+      pkg_extra_dyn <- io (processArgs static_flags pkg_extra_opts [])
 
       -- then, dynamic flags
       io $ do 
        restoreDynFlags
 
       -- then, dynamic flags
       io $ do 
        restoreDynFlags
-        leftovers <- processArgs dynamic_flags leftovers []
+        leftovers <- processArgs dynamic_flags (leftovers ++ pkg_extra_dyn) []
        saveDynFlags
 
         if (not (null leftovers))
        saveDynFlags
 
         if (not (null leftovers))
@@ -675,7 +844,7 @@ unsetOptions str
          then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
          else do
 
          then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
          else do
 
-       mapM unsetOpt plus_opts
+       mapM_ unsetOpt plus_opts
  
        -- can't do GHC flags for now
        if (not (null minus_opts))
  
        -- can't do GHC flags for now
        if (not (null minus_opts))
@@ -709,18 +878,51 @@ optToStr ShowTiming = "s"
 optToStr ShowType   = "t"
 optToStr RevertCAFs = "r"
 
 optToStr ShowType   = "t"
 optToStr RevertCAFs = "r"
 
-newPackages new_pkgs = do
-  state <- getGHCiState
-  dflags <- io getDynFlags
-  cmstate1 <- io (cmUnload (cmstate state) dflags)
+newPackages new_pkgs = do      -- The new packages are already in v_Packages
+  state    <- getGHCiState
+  cmstate1 <- io (cmUnload (cmstate state))
   setGHCiState state{ cmstate = cmstate1, targets = [] }
   setGHCiState state{ cmstate = cmstate1, targets = [] }
+  dflags   <- io getDynFlags
+  io (linkPackages dflags new_pkgs)
+  setContextAfterLoad []
+
+-- ---------------------------------------------------------------------------
+-- code for `:show'
+
+showCmd str =
+  case words str of
+       ["modules" ] -> showModules
+       ["bindings"] -> showBindings
+       ["linker"]   -> io showLinkerState
+       _ -> throwDyn (CmdLineError "syntax:  :show [modules|bindings]")
+
+showModules = do
+  cms <- getCmState
+  let (mg, hpt) = cmGetModInfo cms
+  mapM_ (showModule hpt) mg
+
+
+showModule :: HomePackageTable -> ModSummary -> GHCi ()
+showModule hpt mod_summary
+  = case lookupModuleEnv hpt mod of
+       Nothing       -> panic "missing linkable"
+       Just mod_info -> io (putStrLn (showModMsg obj_linkable mod locn))
+                     where
+                        obj_linkable = isObjectLinkable (hm_linkable mod_info)
+  where
+    mod = ms_mod mod_summary
+    locn = ms_location mod_summary
+
+showBindings = do
+  cms <- getCmState
+  let
+       unqual = cmGetPrintUnqual cms
+--     showBinding b = putStrLn (showSDocForUser unqual (ppr (ifaceTyThing b)))
+       showBinding b = putStrLn (showSDocForUser unqual (ppr (getName b)))
+
+  io (mapM_ showBinding (cmGetBindings cms))
+  return ()
 
 
-  io $ do
-    pkgs <- getPackageInfo
-    flushPackageCache pkgs
-   
-    new_pkg_info <- getPackageDetails new_pkgs
-    mapM_ linkPackage (reverse new_pkg_info)
 
 -----------------------------------------------------------------------------
 -- GHCi monad
 
 -----------------------------------------------------------------------------
 -- GHCi monad
@@ -740,9 +942,6 @@ data GHCiOption
        | RevertCAFs            -- revert CAFs after every evaluation
        deriving Eq
 
        | RevertCAFs            -- revert CAFs after every evaluation
        deriving Eq
 
-GLOBAL_VAR(flush_stdout, error "no flush_stdout", IO ())
-GLOBAL_VAR(flush_stderr, error "no flush_stdout", IO ())
-
 newtype GHCi a = GHCi { unGHCi :: IORef GHCiState -> IO a }
 
 startGHCi :: GHCi a -> GHCiState -> IO a
 newtype GHCi a = GHCi { unGHCi :: IORef GHCiState -> IO a }
 
 startGHCi :: GHCi a -> GHCiState -> IO a
@@ -759,6 +958,10 @@ ghciHandleDyn h (GHCi m) = GHCi $ \s ->
 getGHCiState   = GHCi $ \r -> readIORef r
 setGHCiState s = GHCi $ \r -> writeIORef r s
 
 getGHCiState   = GHCi $ \r -> readIORef r
 setGHCiState s = GHCi $ \r -> writeIORef r s
 
+-- for convenience...
+getCmState = getGHCiState >>= return . cmstate
+setCmState cms = do s <- getGHCiState; setGHCiState s{cmstate=cms}
+
 isOptionSet :: GHCiOption -> GHCi Bool
 isOptionSet opt
  = do st <- getGHCiState
 isOptionSet :: GHCiOption -> GHCi Bool
 isOptionSet opt
  = do st <- getGHCiState
@@ -787,151 +990,12 @@ io m = GHCi { unGHCi = \s -> m >>= return }
 ghciHandle :: (Exception -> GHCi a) -> GHCi a -> GHCi a
 ghciHandle h (GHCi m) = GHCi $ \s -> 
    Exception.catch (m s) 
 ghciHandle :: (Exception -> GHCi a) -> GHCi a -> GHCi a
 ghciHandle h (GHCi m) = GHCi $ \s -> 
    Exception.catch (m s) 
-       (\e -> unGHCi (ghciHandle h (ghciUnblock (h e))) s)
+       (\e -> unGHCi (ghciUnblock (h e)) s)
 
 ghciUnblock :: GHCi a -> GHCi a
 ghciUnblock (GHCi a) = GHCi $ \s -> Exception.unblock (a s)
 
 -----------------------------------------------------------------------------
 
 ghciUnblock :: GHCi a -> GHCi a
 ghciUnblock (GHCi a) = GHCi $ \s -> Exception.unblock (a s)
 
 -----------------------------------------------------------------------------
--- package loader
-
--- Left: full path name of a .o file, including trailing .o
--- Right: "unadorned" name of a .DLL/.so
---        e.g.    On unix     "qt"  denotes "libqt.so"
---                On WinDoze  "burble"  denotes "burble.DLL"
---        addDLL is platform-specific and adds the lib/.so/.DLL
---        suffixes platform-dependently; we don't do that here.
--- 
--- For dynamic objects only, try to find the object file in all the 
--- directories specified in v_Library_Paths before giving up.
-
-type LibrarySpec
-   = Either FilePath String
-
-showLS (Left nm)  = "(static) " ++ nm
-showLS (Right nm) = "(dynamic) " ++ nm
-
-linkPackages :: [LibrarySpec] -> [PackageConfig] -> IO ()
-linkPackages cmdline_lib_specs pkgs
-   = do mapM_ linkPackage (reverse pkgs)
-        lib_paths <- readIORef v_Library_paths
-        mapM_ (preloadLib lib_paths) cmdline_lib_specs
-       if (null cmdline_lib_specs)
-          then return ()
-          else do putStr "final link ... "
-                  ok <- resolveObjs
-                  if ok then putStrLn "done."
-                        else throwDyn (InstallationError 
-                                          "linking extra libraries/objects failed")
-     where
-        preloadLib :: [String] -> LibrarySpec -> IO ()
-        preloadLib lib_paths lib_spec
-           = do putStr ("Loading object " ++ showLS lib_spec ++ " ... ")
-                case lib_spec of
-                   Left static_ish
-                      -> do b <- preload_static lib_paths static_ish
-                            putStrLn (if b then "done." else "not found")
-                   Right dll_unadorned
-                      -> -- We add "" to the set of paths to try, so that
-                         -- if none of the real paths match, we force addDLL
-                         -- to look in the default dynamic-link search paths.
-                         do maybe_errstr <- preload_dynamic (lib_paths++[""]) 
-                                                            dll_unadorned
-                            case maybe_errstr of
-                               Nothing -> return ()
-                               Just mm -> preloadFailed mm lib_paths lib_spec
-                            putStrLn "done"
-
-        preloadFailed :: String -> [String] -> LibrarySpec -> IO ()
-        preloadFailed sys_errmsg paths spec
-           = do putStr ("failed.\nDynamic linker error message was:\n   " 
-                        ++ sys_errmsg  ++ "\nWhilst trying to load:  " 
-                        ++ showLS spec ++ "\nDirectories to search are:\n"
-                        ++ unlines (map ("   "++) paths) )
-                give_up
-
-        -- not interested in the paths in the static case.
-        preload_static paths name
-           = do b <- doesFileExist name
-                if not b then return False
-                         else loadObj name >> return True
-
-        -- return Nothing == success, else Just error message from addDLL
-        preload_dynamic [] name
-           = return Nothing
-        preload_dynamic (path:paths) rootname
-           = do -- addDLL returns NULL on success
-                maybe_errmsg <- addDLL path rootname
-                if    maybe_errmsg == nullPtr
-                 then preload_dynamic paths rootname
-                 else do str <- peekCString maybe_errmsg
-                         return (Just str)
-
-        give_up 
-           = (throwDyn . CmdLineError)
-                "user specified .o/.so/.DLL could not be loaded."
-
--- Packages that don't need loading, because the compiler shares them with
--- the interpreted program.
-dont_load_these = [ "gmp", "rts" ]
-
--- Packages that are already linked into GHCi.  For mingw32, we only
--- skip gmp and rts, since std and after need to load the msvcrt.dll
--- library which std depends on.
-loaded_in_ghci
-#          ifndef mingw32_TARGET_OS
-           = [ "std", "concurrent", "posix", "text", "util" ]
-#          else
-          = [ ]
-#          endif
-
-linkPackage :: PackageConfig -> IO ()
-linkPackage pkg
-   | name pkg `elem` dont_load_these = return ()
-   | otherwise
-   = do 
-        -- For each obj, try obj.o and if that fails, obj.so.
-        -- Complication: all the .so's must be loaded before any of the .o's.  
-        let dirs      =  library_dirs pkg
-        let objs      =  hs_libraries pkg ++ extra_libraries pkg
-        classifieds   <- mapM (locateOneObj dirs) objs
-
-       -- Don't load the .so libs if this is a package GHCi is already
-       -- linked against, because we'll already have the .so linked in.
-       let (so_libs, obj_libs) = partition isRight classifieds
-        let sos_first | name pkg `elem` loaded_in_ghci = obj_libs
-                     | otherwise                      = so_libs ++ obj_libs
-
-       putStr ("Loading package " ++ name pkg ++ " ... ")
-        mapM loadClassified sos_first
-        putStr "linking ... "
-        ok <- resolveObjs
-       if ok then putStrLn "done."
-             else panic ("can't load package `" ++ name pkg ++ "'")
-     where
-        isRight (Right _) = True
-        isRight (Left _)  = False
-
-loadClassified :: LibrarySpec -> IO ()
-loadClassified (Left obj_absolute_filename)
-   = do loadObj obj_absolute_filename
-loadClassified (Right dll_unadorned)
-   = do maybe_errmsg <- addDLL "" dll_unadorned -- doesn't seem right to me
-        if    maybe_errmsg == nullPtr
-         then return ()
-         else do str <- peekCString maybe_errmsg
-                 throwDyn (CmdLineError ("can't load .so/.DLL for: " 
-                                       ++ dll_unadorned ++ " (" ++ str ++ ")" ))
-
-locateOneObj :: [FilePath] -> String -> IO LibrarySpec
-locateOneObj []     obj 
-   = return (Right obj) -- we assume
-locateOneObj (d:ds) obj 
-   = do let path = d ++ '/':obj ++ ".o"
-        b <- doesFileExist path
-        if b then return (Left path) else locateOneObj ds obj
-
------------------------------------------------------------------------------
 -- timing & statistics
 
 timeIt :: GHCi a -> GHCi a
 -- timing & statistics
 
 timeIt :: GHCi a -> GHCi a
@@ -947,7 +1011,7 @@ timeIt action
                  io $ printTimes (allocs2 - allocs1) (time2 - time1)
                  return a
 
                  io $ printTimes (allocs2 - allocs1) (time2 - time1)
                  return a
 
-foreign import "getAllocations" getAllocations :: IO Int
+foreign import ccall "getAllocations" getAllocations :: IO Int
 
 printTimes :: Int -> Integer -> IO ()
 printTimes allocs psecs
 
 printTimes :: Int -> Integer -> IO ()
 printTimes allocs psecs
@@ -960,4 +1024,24 @@ printTimes allocs psecs
 -----------------------------------------------------------------------------
 -- reverting CAFs
        
 -----------------------------------------------------------------------------
 -- reverting CAFs
        
-foreign import revertCAFs :: IO ()     -- make it "safe", just in case
+revertCAFs :: IO ()
+revertCAFs = do
+  rts_revertCAFs
+  turnOffBuffering
+       -- Have to turn off buffering again, because we just 
+       -- reverted stdout, stderr & stdin to their defaults.
+
+foreign import ccall "revertCAFs" rts_revertCAFs  :: IO ()  
+       -- Make it "safe", just in case
+
+-- -----------------------------------------------------------------------------
+-- Utils
+
+expandPath :: String -> GHCi String
+expandPath path = 
+  case dropWhile isSpace path of
+   ('~':d) -> do
+       tilde <- io (getEnv "HOME")     -- will fail if HOME not defined
+       return (tilde ++ '/':d)
+   other -> 
+       return other