Search code examples
haskellstdinhandleio-monad

Portably opening a handle to stdin many times in a single session


Code:

main = do
         putStrLn "4917 Microprocessor\nEnter the Machine Code to be run: "
         inp <- getContents
         putStrLn "The output of the Program is:"
         fState <- ((runStateT _4917) . construct . parse) inp
         args <- getArgs
         if elem "-v" args then putStrLn ("\nFinal state was: " ++ (show . snd) fState) else return ()
         putStrLn "\n================================ RESTART ================================"
         main
        where parse xs = array (0,15) $
                         zip [0..15] $
                         take 16 $
                         map ((makeArbInt 4) . read) (words (filter ((/=)'.') xs)) ++ repeat (makeArbInt 4 0)
              construct xs = Program xs z z z z 0 False
              z = (makeArbInt 4 0)

There's more but this is the relevant part. Basically line 3 needs to be evaluated multiple times but getContents is closing the stdin handle:

4917: <stdin>: hGetContents: illegal operation (handle is closed)

Is there a way to reopen the handle? Or some way of preventing getContents from doing that? (Maybe I'm sending the wrong signal. I'm sending over a Ctrl-D EOF on Linux. Maybe I should use EOT or something instead?)

edit: I've managed to get the desired behaviour but it won't port to windows.

mystdin <- openFile "/dev/tty" ReadMode
inp <- hGetContents mystdin

New question: is there a generic way to portably open a handle to stdin?


Solution

  • To open a handle to stdin portably, use hDuplicate function on the existing stdio handle to get a new one:

    mystdin <- hDuplicate stdin
    inp <- hGetContents mystdin
    

    Make sure never to close the original stdin, so that you can make more duplicates as appropriate. (I'm not sure if this is good Haskell style)