
Graham Fawcett wrote:
I would like to communicate with an external, line-oriented process, which takes a sequence of one-line commands, each returning an arbitrary number of lines, and waits for another command after each response. So, something like: sendCmd :: (Handle, Handle) -> String -> IO [String]
You may need to make some compromises to get this within the realm of the possible, if I understand your objective. - There is no way (at least on UNIX) to know when a read has been posted on the other end of your pipe/socket/pty/whatever. So you can't tell in this way when the external process has sent the last of the arbitrary number of lines and is now waiting for another command. If you have another way to know, or it doesn't really matter, then fine. - The vast majority of `line-oriented' software are actually going to block buffer their output when writing to a pipe, because that's what C I/O does. If you're lucky, the program you're dealing with here will flush its output before it reads, but if it doesn't, you're hosed - there isn't any way to talk to this program `interactively' on a pipe. In this case, you need a pseudotty device, a sort of pipe that supports tty device ioctls. - Buffering on your side of the I/O is of course also worse than useless. I see the GHC 6.8 library supports pseudottys, so for general amusement I submit below a small demonstration program. Unfortunately it doesn't entirely work. The pseudotty works, but I'm unable to turn off ECHO on the slave. So each master line yields two slave lines, and my program expects only one and gets behind on that account. So the command has to include its own "stty -echo". The commented lines attempt to turn of ECHO, but on MacOS X that causes the program to fail mysteriously. Donn Cave, donn@avvanta.com -------------------------------------------------- import System.Posix.Terminal (TerminalMode(..), TerminalState(..), withoutMode, getTerminalAttributes, setTerminalAttributes, openPseudoTerminal, getSlaveTerminalName) import System (getArgs) import System.Posix.Types (Fd, ProcessID) import System.Posix.Process (forkProcess, executeFile) import System.Posix.IO (stdInput, stdOutput, closeFd, dupTo, fdWrite, fdRead) pchild :: Fd -> IO () -> IO () pchild slaveFd exec = do dupTo slaveFd stdInput dupTo slaveFd stdOutput closeFd slaveFd exec ptyOpen :: IO () -> IO (ProcessID, Fd) ptyOpen exec = do (master, slave) <- openPseudoTerminal -- tc <- getTerminalAttributes slave -- let tc = withoutMode tc EchoLF -- setTerminalAttributes slave tc WhenDrained pid <- forkProcess (pchild slave exec) closeFd slave return (pid, master) ioact p0 p1 m0 m1 = do (d, n) <- fdRead m0 512 fdWrite p1 d (e, n) <- fdRead p0 512 fdWrite m1 e ioact p0 p1 m0 m1 main = do (cmd:args) <- getArgs (pid, fd) <- ptyOpen (executeFile cmd True args Nothing) ioact fd fd stdInput stdOutput