[project @ 1999-07-29 13:57:34 by sof]
[ghc-hetmet.git] / ghc / lib / misc / BSD.lhs
index fae2966..2b07ebc 100644 (file)
@@ -3,70 +3,92 @@
 %
 \section[BSD]{Misc BSD bindings}
 
-The @BSD@ module defines Haskell bindings to network programming
-functionality that is only provided by BSD-style APIs.
+The @BSD@ module defines Haskell bindings to functionality
+provided by BSD Unix derivatives. Currently this covers
+network programming functionality and symbolic links.
+(OK, so the latter is pretty much supported by most *nixes
+today, but it was BSD that introduced them.)
 
 \begin{code}       
-{-# OPTIONS -#include "cbits/ghcSockets.h" #-}
+{-# OPTIONS -#include "cbits/ghcSockets.h" -#include "stgio.h" #-}
+
+#include "config.h"
 
 module BSD (
        
     HostName,
     getHostName,           -- :: IO HostName
 
+    ServiceEntry(..),
     ServiceName,
-    getServiceByName,      -- :: ServiceName -> IO ServiceEntry
+    getServiceByName,      -- :: ServiceName -> ProtocolName -> IO ServiceEntry
+    getServiceByPort,       -- :: PortNumber  -> ProtocolName -> IO ServiceEntry
     getServicePortNumber,   -- :: ServiceName -> IO PortNumber
 
-    ServiceEntry(..),
+#ifndef _WIN32
     getServiceEntry,       -- :: IO ServiceEntry
     setServiceEntry,       -- :: Bool -> IO ()
     endServiceEntry,       -- :: IO ()
     getServiceEntries,     -- :: Bool -> IO [ServiceEntry]
+#endif
 
     ProtocolName,
+    ProtocolNumber,
     ProtocolEntry(..),
     getProtocolByName,     -- :: ProtocolName   -> IO ProtocolEntry
     getProtocolByNumber,    -- :: ProtocolNumber -> IO ProtcolEntry
+    getProtocolNumber,     -- :: ProtocolName   -> ProtocolNumber
 
+#ifndef _WIN32
     setProtocolEntry,      -- :: Bool -> IO ()
     getProtocolEntry,      -- :: IO ProtocolEntry
     endProtocolEntry,      -- :: IO ()
     getProtocolEntries,            -- :: Bool -> IO [ProtocolEntry]
+#endif
 
     PortNumber,
-    getProtocolNumber,     -- :: ProtocolName -> ProtocolNumber
+    mkPortNumber,          -- :: Int -> PortNumber
 
     HostEntry(..),
     getHostByName,         -- :: HostName -> IO HostEntry
     getHostByAddr,         -- :: HostAddress -> Family -> IO HostEntry
+    hostAddress,           -- :: HostEntry -> HostAddress
 
+#ifndef _WIN32
     setHostEntry,          -- :: Bool -> IO ()
     getHostEntry,          -- :: IO HostEntry
     endHostEntry,          -- :: IO ()
     getHostEntries,        -- :: Bool -> IO [HostEntry]
+#endif
 
     NetworkName,
     NetworkAddr,
-    NetworkEntry(..),
-    getNetworkByName,      -- :: NetworkName -> IO NetworkEntry
-    getNetworkByAddr,       -- :: NetworkAddr -> Family -> IO NetworkEntry
-    setNetworkEntry,       -- :: Bool -> IO ()
-    getNetworkEntry,       -- :: IO NetworkEntry
-    endNetworkEntry,       -- :: IO ()
-    getNetworkEntries       -- :: Bool -> IO [NetworkEntry]
-    
-) where
+    NetworkEntry(..)
+#ifndef _WIN32
+    , getNetworkByName     -- :: NetworkName -> IO NetworkEntry
+    , getNetworkByAddr     -- :: NetworkAddr -> Family -> IO NetworkEntry
+    , setNetworkEntry      -- :: Bool -> IO ()
+    , getNetworkEntry      -- :: IO NetworkEntry
+    , endNetworkEntry      -- :: IO ()
+    , getNetworkEntries     -- :: Bool -> IO [NetworkEntry]
+#endif
+
+#ifdef HAVE_SYMLINK
+    , symlink              -- :: String -> String -> IO ()
+#endif
+#ifdef HAVE_READLINK
+    , readlink             -- :: String -> IO String
+#endif
+
+    ) where
 
 
 import GlaExts
+import PrelIOBase ( IOError (..), IOErrorType(..) )
 
-import PrelIOBase
-
-import Foreign  -- Addr..
-import PackedString ( byteArrayToPS, unpackPS )
-  
-import PosixUtil  ( strcpy, unvectorize )
+import Foreign
+import Addr
+import CString ( unpackCStringIO, unpackCStringBA, unvectorize, unpackNBytesBA )
 import SocketPrim
 
 \end{code}
@@ -81,29 +103,37 @@ import SocketPrim
 \begin{code}
 type HostName = String
 type ProtocolName = String
-type ProtocolNumber = Int
 type ServiceName = String
-type PortNumber = Int
 
 data ProtocolEntry = 
-  ProtocolEntry
-     ProtocolName      -- Official Name
-     [ProtocolName]    -- aliases
-     Int               -- Protocol Number
+  ProtocolEntry  {
+     protoName    :: ProtocolName,     -- Official Name
+     protoAliases :: [ProtocolName],   -- aliases
+     protoNumber  :: ProtocolNumber    -- Protocol Number
+  }
 
 data ServiceEntry  = 
-  ServiceEntry
-     ServiceName       -- Official Name
-     [ServiceName]     -- aliases
-     PortNumber                -- Port Number
-     ProtocolName      -- Protocol
+  ServiceEntry  {
+     serviceName     :: ServiceName,   -- Official Name
+     serviceAliases  :: [ServiceName], -- aliases
+     servicePort     :: PortNumber,    -- Port Number  ( network byte order )
+     serviceProtocol :: ProtocolName   -- Protocol
+  }
+
 data HostEntry = 
-  HostEntry
-     HostName          -- Official Name
-     [HostName]                -- aliases
-     Family            -- Host Type (currently AF_INET)
-     [HostAddress]     -- Set of Network Addresses
+  HostEntry  {
+     hostName      :: HostName,        -- Official Name
+     hostAliases   :: [HostName],      -- aliases
+     hostFamily    :: Family,          -- Host Type (currently AF_INET)
+     hostAddresses :: [HostAddress]    -- Set of Network Addresses  (in network byte order)
+  }
+
+-- convenience function:
+hostAddress :: HostEntry -> HostAddress
+hostAddress (HostEntry nm _ _ ls) =
+ case ls of
+   []    -> error ("BSD.hostAddress: empty network address list for " ++ nm)
+   (x:_) -> x
 
 \end{code}
 
@@ -130,17 +160,17 @@ getServiceByName :: ServiceName   -- Service Name
                 -> IO ServiceEntry     -- Service Entry
 getServiceByName name proto = do
  ptr <- _ccall_ getservbyname name proto
- if ptr == ``NULL'' 
-    then fail (IOError Nothing NoSuchThing "no such service entry")
+ if ptr == nullAddr
+    then ioError (IOError Nothing NoSuchThing "getServiceByName" "no such service entry")
     else unpackServiceEntry ptr
 
-getServiceByPort :: PortNumber ->      
-                   ProtocolName ->
-                   IO ServiceEntry
-getServiceByPort port proto = do
+getServiceByPort :: PortNumber
+                -> ProtocolName
+                -> IO ServiceEntry
+getServiceByPort (PNum port) proto = do
     ptr <- _ccall_ getservbyport port proto
-    if ptr == ``NULL'' 
-       then fail (IOError Nothing NoSuchThing "no such service entry")
+    if ptr == nullAddr
+       then ioError (IOError Nothing NoSuchThing "getServiceByPort" "no such service entry")
        else unpackServiceEntry ptr
                   
 getServicePortNumber :: ServiceName -> IO PortNumber
@@ -148,16 +178,17 @@ getServicePortNumber name = do
     (ServiceEntry _ _ port _) <- getServiceByName name "tcp"
     return port
 
+#ifndef _WIN32
 getServiceEntry        :: IO ServiceEntry
 getServiceEntry = do
     ptr <- _ccall_ getservent
-    if ptr == ``NULL'' 
-       then fail (IOError Nothing NoSuchThing "no such service entry")
+    if ptr == nullAddr
+       then ioError (IOError Nothing NoSuchThing "getServiceEntry" "no such service entry")
        else unpackServiceEntry ptr
 
 setServiceEntry        :: Bool -> IO ()
 setServiceEntry flg = _ccall_ setservent stayOpen
- where stayOpen = if flg then 1 else 0
+ where stayOpen = (if flg then 1 else 0) :: Int
 
 endServiceEntry        :: IO ()
 endServiceEntry = _ccall_ endservent
@@ -166,7 +197,7 @@ getServiceEntries :: Bool -> IO [ServiceEntry]
 getServiceEntries stayOpen = do
   setServiceEntry stayOpen
   getEntries (getServiceEntry) (endServiceEntry)
-
+#endif
 \end{code}
 
 The following relate directly to the corresponding \tr{UNIX} {C} calls for
@@ -179,29 +210,31 @@ determines whether or not the protocol database file, usually
 @getProtocolEntry@. Similarly, 
 
 \begin{code}
-getProtocolByName   :: ProtocolName -> IO ProtocolEntry
-getProtocolByNumber :: PortNumber   -> IO ProtocolEntry
-getProtocolNumber   :: ProtocolName -> IO ProtocolNumber
+getProtocolByName   :: ProtocolName   -> IO ProtocolEntry
+getProtocolByNumber :: ProtocolNumber -> IO ProtocolEntry
+getProtocolNumber   :: ProtocolName   -> IO ProtocolNumber
 
+#ifndef _WIN32
 setProtocolEntry    :: Bool -> IO ()   -- Keep DB Open ?
 getProtocolEntry    :: IO ProtocolEntry        -- Next Protocol Entry from DB
 endProtocolEntry    :: IO ()
 getProtocolEntries  :: Bool -> IO [ProtocolEntry]
+#endif
 \end{code}
 
 \begin{code}
 --getProtocolByName :: ProtocolName -> IO ProtocolEntry
 getProtocolByName name = do
  ptr <- _ccall_ getprotobyname name
- if (ptr == ``NULL'' )
-    then fail (IOError Nothing NoSuchThing "no such protocol entry")
+ if ptr == nullAddr
+    then ioError (IOError Nothing NoSuchThing "getProtocolByName" "no such protocol entry")
     else unpackProtocolEntry ptr
 
---getProtocolByNumber :: PortNumber -> IO ProtocolEntry
+--getProtocolByNumber :: ProtocolNumber -> IO ProtocolEntry
 getProtocolByNumber num = do
  ptr <- _ccall_ getprotobynumber num
- if ptr == ``NULL''
-    then fail (IOError Nothing NoSuchThing "no such protocol entry")
+ if ptr == nullAddr
+    then ioError (IOError Nothing NoSuchThing "getProtocolByNumber" "no such protocol entry")
     else unpackProtocolEntry ptr
 
 --getProtocolNumber :: ProtocolName -> IO ProtocolNumber
@@ -209,16 +242,17 @@ getProtocolNumber proto = do
  (ProtocolEntry _ _ num) <- getProtocolByName proto
  return num
 
+#ifndef _WIN32
 --getProtocolEntry :: IO ProtocolEntry -- Next Protocol Entry from DB
 getProtocolEntry = do
  ptr <- _ccall_ getprotoent
- if ptr == ``NULL'' 
-    then fail (IOError Nothing NoSuchThing "no such protocol entry")
+ if ptr == nullAddr
+    then ioError (IOError Nothing NoSuchThing "getProtocolEntry" "no such protocol entry")
     else unpackProtocolEntry ptr
 
 --setProtocolEntry :: Bool -> IO ()    -- Keep DB Open ?
 setProtocolEntry flg = _ccall_ setprotoent v
- where v = if flg then 1 else 0
+ where v = (if flg then 1 else 0) :: Int
 
 --endProtocolEntry :: IO ()
 endProtocolEntry = _ccall_ endprotoent
@@ -227,6 +261,7 @@ endProtocolEntry = _ccall_ endprotoent
 getProtocolEntries stayOpen = do
   setProtocolEntry stayOpen
   getEntries (getProtocolEntry) (endProtocolEntry)
+#endif
 
 \end{code}
 
@@ -234,31 +269,32 @@ getProtocolEntries stayOpen = do
 getHostByName :: HostName -> IO HostEntry
 getHostByName name = do
     ptr <- _ccall_ gethostbyname name
-    if ptr == ``NULL''
-       then fail (IOError Nothing NoSuchThing "no such host entry")
+    if ptr == nullAddr
+       then ioError (IOError Nothing NoSuchThing "getHostByName" "no such host entry")
        else unpackHostEntry ptr
 
 getHostByAddr :: Family -> HostAddress -> IO HostEntry
 getHostByAddr family addr = do
  ptr <- _casm_ ``struct in_addr addr;
-                addr.s_addr = htonl(%0);
+                addr.s_addr = %0;
                 %r = gethostbyaddr ((char*)&addr, sizeof(struct in_addr), %1);''
                addr
                (packFamily family)
- if ptr == ``NULL'' 
-    then fail (IOError Nothing NoSuchThing "no such host entry")
+ if ptr == nullAddr
+    then ioError (IOError Nothing NoSuchThing "getHostByAddr" "no such host entry")
     else unpackHostEntry ptr
 
+#ifndef _WIN32
 getHostEntry :: IO HostEntry
 getHostEntry = do
  ptr <- _ccall_ gethostent
- if ptr == ``NULL'' 
-    then fail (IOError Nothing NoSuchThing "unable to retrieve host entry")
+ if ptr == nullAddr
+    then ioError (IOError Nothing NoSuchThing "getHostEntry" "unable to retrieve host entry")
     else unpackHostEntry ptr
 
 setHostEntry :: Bool -> IO ()
 setHostEntry flg = _ccall_ sethostent v
- where v = if flg then 1 else 0
+ where v = (if flg then 1 else 0) :: Int
 
 endHostEntry :: IO ()
 endHostEntry = _ccall_ endhostent
@@ -267,7 +303,7 @@ getHostEntries :: Bool -> IO [HostEntry]
 getHostEntries stayOpen = do
   setHostEntry stayOpen
   getEntries (getHostEntry) (endHostEntry)
-
+#endif
 \end{code}
 
 %***************************************************************************
@@ -280,43 +316,43 @@ Same set of access functions as for accessing host,protocol and service
 system info, this time for the types of networks supported.
 
 \begin{code}
+-- network addresses are represented in host byte order.
 type NetworkAddr = Word
+
 type NetworkName = String
 
 data NetworkEntry =
-  NetworkEntry
-     NetworkName   -- official name
-     [NetworkName] -- aliases
-     Family       -- type
-     NetworkAddr
-
+  NetworkEntry {
+     networkName       :: NetworkName,   -- official name
+     networkAliases    :: [NetworkName], -- aliases
+     networkFamily     :: Family,         -- type
+     networkAddress    :: NetworkAddr
+   }
+#ifndef _WIN32
 getNetworkByName :: NetworkName -> IO NetworkEntry
 getNetworkByName name = do
  ptr <- _ccall_ getnetbyname name
- if ptr == ``NULL'' 
-    then fail (IOError Nothing NoSuchThing "no such network entry")
+ if ptr == nullAddr
+    then ioError (IOError Nothing NoSuchThing "getNetworkByName" "no such network entry")
     else unpackNetworkEntry ptr
 
 getNetworkByAddr :: NetworkAddr -> Family -> IO NetworkEntry
 getNetworkByAddr addr family = do
- ptr <-  _casm_ ``long naddr = htonl(%0);
-                 %r = getnetbyaddr (naddr, (int)%1);''
-                addr
-                (packFamily family)
- if ptr == ``NULL''
-    then fail (IOError Nothing NoSuchThing "no such network entry")
+ ptr <-  _ccall_ getnetbyaddr addr (packFamily family)
+ if ptr == nullAddr
+    then ioError (IOError Nothing NoSuchThing "getNetworkByAddr" "no such network entry")
     else unpackNetworkEntry ptr
 
 getNetworkEntry :: IO NetworkEntry
 getNetworkEntry = do
  ptr <- _ccall_ getnetent
- if ptr == ``NULL'' 
-   then fail (IOError Nothing NoSuchThing "no more network entries")
+ if ptr == nullAddr
+   then ioError (IOError Nothing NoSuchThing "getNetworkEntry" "no more network entries")
    else unpackNetworkEntry ptr
 
 setNetworkEntry :: Bool -> IO ()
 setNetworkEntry flg = _ccall_ setnetent v
- where v = if flg then 1 else 0
+ where v = (if flg then 1 else 0) :: Int
 
 endNetworkEntry :: IO ()
 endNetworkEntry = _ccall_ endnetent
@@ -325,6 +361,7 @@ getNetworkEntries :: Bool -> IO [NetworkEntry]
 getNetworkEntries stayOpen = do
   setNetworkEntry stayOpen
   getEntries (getNetworkEntry) (endNetworkEntry)
+#endif
 
 \end{code}
 
@@ -342,10 +379,11 @@ getHostName :: IO HostName
 getHostName = do
   ptr <- stToIO (newCharArray (0,256))
   rc  <- _casm_ ``%r=gethostname(%0, 256);'' ptr
-  ba  <- stToIO (unsafeFreezeByteArray ptr)
-  if rc == -1 
-     then fail (userError "getHostName: unable to determine host name")
-     else return (unpackPS (byteArrayToPS ba))
+  if rc == ((-1)::Int)
+     then ioError (userError "getHostName: unable to determine host name")
+     else do
+       ba  <- stToIO (unsafeFreezeByteArray ptr)
+       return (unpackCStringBA ba)
 \end{code}
 
 Helper function used by the exported functions that provides a
@@ -386,23 +424,21 @@ getEntries getOne atEnd = loop
 \begin{code}
 unpackServiceEntry :: Addr -> PrimIO ServiceEntry
 unpackServiceEntry ptr = do
- str     <- _casm_ ``%r = ((struct servent*)%0)->s_name;'' ptr
- name    <- strcpy str
+ pname   <- _casm_ ``%r = ((struct servent*)%0)->s_name;'' ptr
+ name    <- unpackCStringIO pname
  alias   <- _casm_ ``%r = ((struct servent*)%0)->s_aliases;'' ptr
  aliases <- unvectorize alias 0
-  -- Note: port numbers are represented as ints in (struct servent), but
-  -- inet port numbers are 16-bit, hence the use of ntohs() rather than ntohl()
- port    <- _casm_ ``%r = (int)ntohs((int)(((struct servent*)%0)->s_port));'' ptr
+ port    <- _casm_ ``%r = (int)(((struct servent*)%0)->s_port);'' ptr
  str     <- _casm_ ``%r = (char *)((struct servent*)%0)->s_proto;'' ptr
- proto   <- strcpy str
- return (ServiceEntry name aliases port proto)
+ proto   <- unpackCStringIO str
+ return (ServiceEntry name aliases (PNum port) proto)
 
 -------------------------------------------------------------------------------
 
 unpackProtocolEntry :: Addr -> IO ProtocolEntry
 unpackProtocolEntry ptr = do
  str     <- _casm_ ``%r = ((struct protoent*)%0)->p_name;'' ptr
- name    <- strcpy str
+ name    <- unpackCStringIO str
  alias   <- _casm_ ``%r = ((struct protoent*)%0)->p_aliases;'' ptr
  aliases <- unvectorize alias 0
  proto   <- _casm_ ``%r = ((struct protoent*)%0)->p_proto;'' ptr
@@ -413,7 +449,7 @@ unpackProtocolEntry ptr = do
 unpackHostEntry :: Addr -> IO HostEntry
 unpackHostEntry ptr = do
   str      <- _casm_ ``%r = ((struct hostent*)%0)->h_name;'' ptr
-  name     <- strcpy str
+  name     <- unpackCStringIO str
   alias    <- _casm_ ``%r = ((struct hostent*)%0)->h_aliases;'' ptr
   aliases  <- unvectorize alias 0
   addrList <- unvectorizeHostAddrs ptr 0
@@ -424,7 +460,7 @@ unpackHostEntry ptr = do
 unpackNetworkEntry :: Addr -> IO NetworkEntry
 unpackNetworkEntry ptr = do
   str     <- _casm_ ``%r = ((struct netent*)%0)->n_name;'' ptr
-  name    <- strcpy str
+  name    <- unpackCStringIO str
   alias   <- _casm_ ``%r = ((struct netent*)%0)->n_aliases;'' ptr
   aliases <- unvectorize alias 0
   fam     <- _casm_ ``%r = ((struct netent*)%0)->n_addrtype;'' ptr
@@ -433,19 +469,60 @@ unpackNetworkEntry ptr = do
 
 -------------------------------------------------------------------------------
 
-unvectorizeHostAddrs :: Addr -> Int -> IO [Word]
-unvectorizeHostAddrs ptr n 
-  | str == ``NULL'' = return []
-  | otherwise = do
-       x <- _casm_ ``{ u_long tmp;
+unvectorizeHostAddrs :: Addr -> Int -> IO [HostAddress]
+unvectorizeHostAddrs ptr n  = do
+       x <- _casm_ ``{ unsigned long tmp;
                   if ((((struct hostent*)%0)->h_addr_list[(int)%1]) == NULL)
                      tmp=(W_)0;
                   else
-                     tmp = (W_)ntohl(((struct in_addr *)(((struct hostent*)%0)->h_addr_list[(int)%1]))->s_addr); 
+                     tmp = (W_)((struct in_addr *)(((struct hostent*)%0)->h_addr_list[(int)%1]))->s_addr; 
                   %r=(W_)tmp;} ''
                ptr n
-       xs <- unvectorizeHostAddrs ptr (n+1)
-       return (x : xs)
-  where str = indexAddrOffAddr ptr n
+       if x == (W# (int2Word# 0#))
+        then return []
+        else do
+          xs <- unvectorizeHostAddrs ptr (n+1)
+          return (x : xs)
+
+
+\end{code}
+
+%***************************************************************************
+%*                                                                         *
+\subsection[BSD-symlink]{Symbolic links}
+%*                                                                         *
+%***************************************************************************
+
+
+\begin{code}
+#ifdef HAVE_SYMLINK
+symlink :: String -> String -> IO ()
+symlink actual_path sym_path = do
+   rc <- _ccall_ symlink actual_path sym_path
+   if rc == (0::Int) then
+      return ()
+    else do
+      _ccall_ convertErrno
+      cstr <- _ccall_ getErrStr__
+      estr <- unpackCStringIO cstr
+      ioError (userError ("BSD.symlink: " ++ estr))
+#endif
+
+#ifdef HAVE_READLINK
+readlink :: String -> IO String
+readlink sym = do
+   mbuf <- stToIO (newCharArray (0, path_max))
+   buf  <- stToIO (unsafeFreezeByteArray mbuf)
+   rc  <- _ccall_ readlink sym buf (path_max + 1)
+   if rc /= -1 then
+      return (unpackNBytesBA buf rc)
+    else do
+      _ccall_ convertErrno
+      cstr <- _ccall_ getErrStr__
+      estr <- unpackCStringIO cstr
+      ioError (userError ("BSD.readlink: " ++ estr))
+ where
+  path_max = (``PATH_MAX''::Int)
+#endif
 
 \end{code}