--  popen-like library
--
--  Author : Jens-Ulrik Petersen
--  Created: 16 August 2001
--
--  Version:  $Revision: 1.5 $ from $Date: 2001/10/17 07:30:53 $
--
--  Copyright (c) 2001 Jens-Ulrik Holger Petersen
--  (c) The GRASP/AQUA Project, Glasgow University, 1995-1996
--
--  This library is free software; you can redistribute it and/or
--  modify it under the terms of the GNU Library General Public
--  License as published by the Free Software Foundation; either
--  version 2 of the License, or (at your option) any later version.
--
--  This library is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
--  Library General Public License for more details.
--
-- Description
--

-- This code is based on runProcess from the hslibs posix
-- library, but internally it uses file descriptors instead
-- of handles and returns the output and error streams
-- lazily as strings as well as the pid of forked process,
-- instead of just IO ().

module System.Posix.POpen (popen, popenEnvDir)
where

import System.Posix.Types (Fd(), ProcessID())
import System.Posix.IO (createPipe, fdToHandle, closeFd, dupTo, stdInput, stdOutput, stdError)
-- import Control.Concurrent (forkIO)
import System.Posix.Process (executeFile)
import System.Directory (setCurrentDirectory)
import System.IO (hGetContents, hPutStr, hClose)
import Data.Maybe (fromJust, isJust)
import Control.Monad (when)

popen :: FilePath                        -- Command
      -> [String]                        -- Arguments
      -> Maybe String                    -- Input
      -> IO (String, String, ProcessID)  -- (stdout, stderr, pid)
popen path args inpt =
    popenEnvDir path args inpt Nothing Nothing

popenEnvDir :: FilePath                  -- Command
              -> [String]                        -- Arguments
              -> Maybe String                    -- Input
              -> Maybe [(String, String)]        -- Environment
              -> Maybe FilePath                  -- Working directory
                        -- (stdin, stdout, stderr, pid)
              -> IO (String, String, ProcessID)
popenEnvDir path args inpt env dir =
    do
    inr <- if (isJust inpt) then
             do
             (inr', inw) <- createPipe
             hin <- fdToHandle inw
             hPutStr hin $ fromJust inpt
             hClose hin
             return $ Just inr'
            else
            return Nothing
    (outr, outw) <- createPipe
    (errr, errw) <- createPipe
--    pid <- forkIO
    p <- doTheBusiness inr outw errw
    do
          -- close other end of pipes in here
          when (isJust inr) $
               closeFd $ fromJust inr
          closeFd outw
          closeFd errw
          hout <- fdToHandle outr
          outstrm <- hGetContents hout
          herr <- fdToHandle errr
          errstrm <- hGetContents herr
          return (outstrm, errstrm, (third p))
    where
    third (_,_,c) = c
    doTheBusiness ::
        Maybe Fd            -- stdin
        -> Fd               -- stdout
        -> Fd               -- stderr
        -> IO (String, String, ProcessID)    -- (stdout, stderr)
    doTheBusiness inr outw errw =
        do
        maybeChangeWorkingDirectory dir
        when (isJust inr) $
             dupTo (fromJust inr) stdInput >> return ()
        dupTo outw stdOutput
        dupTo errw stdError
        executeFile path True args env
        -- for typing, should never actually run
        error "executeFile failed!"

maybeChangeWorkingDirectory :: Maybe FilePath -> IO ()
maybeChangeWorkingDirectory dir =
    case dir of
             Nothing -> return ()
             Just x  -> setCurrentDirectory x