module System.Process.Popen
  (
    popen,
    pclose,
    fileno,
    CStream,
    PopenMode(..),
    withCStream
  )
where

import Foreign.C.String
import Foreign.C.Types
import Foreign.Ptr

newtype CStream = CStream (Ptr ())
data PopenMode = R | W deriving (Eq, Show)

-- | Creates a stream, analog of C's popen
popen :: String -> PopenMode -> IO CStream
popen s mode =
  withCString s
   (\sPtr ->
       withCString (case mode of
                     R -> "r"
                     W -> "w")
         (\modePtr -> popenShim sPtr modePtr >>= return . CStream))

-- | Closes a stream. Returns `Left ()` if the status code is -1 and the status code otherwise.
pclose :: CStream -> IO (Either () CInt)
pclose (CStream streamPtr) = do
  status <- pcloseShim streamPtr
  case status of
    (-1) -> return (Left ())
    _ -> return (Right status)

-- | Gets the file descriptor number from the stream.
fileno :: CStream -> IO CInt
fileno (CStream streamPtr) = filenoShim streamPtr

withCStream :: CStream -> (Ptr () -> IO a) -> IO a
withCStream (CStream streamPtr) f = f streamPtr

foreign import ccall safe "cbits/PopenShim.H popenShim" popenShim ::
    Ptr CChar -> Ptr CChar -> IO (Ptr ())

foreign import ccall safe "cbits/popenShim.H filenoShim" filenoShim ::
    Ptr () -> IO CInt

foreign import ccall safe "cbits/popenShim.H pcloseShim" pcloseShim ::
    Ptr () -> IO CInt